ART虚拟机的一个缺陷问题解决[Java 8 特性],art虚拟机


1.问题背景:

[Android Version]:8.0

[问题] CTS:tck.java.time.chrono.TCKChronoLocalDateTime#test_from_TemporalAccessor

[CTS CMD]:run cts -m CtsLibcoreOjTestCases -t tck.java.time.chrono.TCKChronoLocalDateTime#test_from_TemporalAccessor

[现象]:在问题修复之前,跑上述 CTS CASE会抛出两条 FAILED,查看log,发现 CTS 进程 FC了。

[FC场景]:

6

5239  5256 F libc    : Fatal signal 11 (SIGSEGV), code 1, fault addr 0x12e444080000000c in tid 5256 (roidJUnitRunner)
5265  5265 I crash_dump64: obtaining output fd from tombstoned
1031  1031 I /system/bin/tombstoned: received crash request for pid 5239                                                                                                                     
5265  5265 I crash_dump64: performing dump of process 5239 (target tid = 5256)
5265  5265 F DEBUG   : *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
5265  5265 F DEBUG   : Build fingerprint: '×××:user/release-keys'
5265  5265 F DEBUG   : Revision: '0'
5265  5265 F DEBUG   : ABI: 'arm64'
5265  5265 F DEBUG   : pid: 5239, tid: 5256, name: roidJUnitRunner  >>> android.libcore.runner <<<
5265  5265 F DEBUG   : signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x12e444080000000c
5265  5265 F DEBUG   :     x0   0000007681439380  x1   000000768b74a981  x2   0000000000000025  x3   000000768b74a9a6
5265  5265 F DEBUG   :     x4   000000768b74a9a6  x5   000000768145a999  x6   6d656c706d69202c  x7   6874656d20746e65
5265  5265 F DEBUG   :     x8   ec5e66fe9749eb3b  x9   ec5e66fe9749eb3b  x10  6d20746e656d656c  x11  656420646f687465
5265  5265 F DEBUG   :     x12  646f6874656d5f78  x13  203a7865646e695f  x14  000000768ddaf604  x15  0000000000000000
5265  5265 F DEBUG   :     x16  000000768b7cb620  x17  000000768dd218b0  x18  0000000000000020  x19  000000006fecf480
5265  5265 F DEBUG   :     x20  0000007681746f60  x21  0000007671fff598  x22  000000768d242eb0  x23  12e4440800000000
5265  5265 F DEBUG   :     x24  0000000000000009  x25  0000000000000002  x26  0000007671ff7ad8  x27  000000768d242f70
5265  5265 F DEBUG   :     x28  0000007671ff7ad8  x29  0000007671ff7b70  x30  000000768b6d6cac
5265  5265 F DEBUG   :     sp   0000007671ff7970  pc   000000768b6d6cac  pstate 0000000060000000
5265  5265 F DEBUG   :
5265  5265 F DEBUG   : backtrace:
5265  5265 F DEBUG   :     #00 pc 00000000004ebcac  /system/lib64/libart.so (artInvokeInterfaceTrampoline+4712)
5265  5265 F DEBUG   :     #01 pc 0000000000510494  /system/lib64/libart.so (art_quick_imt_conflict_trampoline+148)
5265  5265 F DEBUG   :     #02 pc 00000000005b6f20  /system/framework/arm64/boot-core-oj.oat (offset 0x2e4000) (java.time.chrono.ChronoLocalDateTime.from+832)
5265  5265 F DEBUG   :     #03 pc 0000000000507638  /system/lib64/libart.so (art_quick_invoke_static_stub+600)
5265  5265 F DEBUG   :     #04 pc 00000000000d804c  /system/lib64/libart.so (_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+260)
5265  5265 F DEBUG   :     #05 pc 0000000000281d98  /system/lib64/libart.so (_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPKNS_7DexFile8CodeItemEPNS_11ShadowFrameEPNS_6JValueE+352)
32bit:
pid: 6313, tid: 6329, name: roidJUnitRunner  >>> android.libcore.runner <<<
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0
Cause: null pointer dereference
    r0 133ccd38  r1 12dc7b80  r2 12e10598  r3 00000002
    r4 00000000  r5 12e10598  r6 12dc8ba0  r7 12dc7b80
    r8 0000004e  r9 e3bcf000  sl 00000000  fp d62fabfc
    ip 00000000  sp d62fabc0  lr 72e1ba5b  pc 00000000  cpsr 600d0010
backtrace:
    #00 pc 00000000  
    #01 pc 72e1ba59  /data/dalvik-cache/arm/system@framework@boot.oat (offset 0x1177000)

从backtrace来看, 32bit 和 64bit 时的 FC backtrace不同,我们接下来先从 32bit 入手分析(后续证明 32bit和 64bit FC原因相同)。

 

2.在32 bit 场景下的FC分析

我们按照正常分析问题的流程来分析该问,先看下 32bit FC 的 tombstone,看看能得到什么信息:

1.fault addr 0x0 :

这个地址能说明问题可能是个 NullPointer 问题,“Cause: null pointer dereference” 也说明了推测的可能原因,这个只是参考,具体怎么引起的还要看最终的分析;

简单说下这个打印:

if (si.si_signo == SIGSEGV && si.si_code == SEGV_MAPERR) {
  if (si.si_addr < reinterpret_cast(4096)) {
    cause = StringPrintf("null pointer dereference");
.....

当FC是 SIGSEGV且 code是 SEGV_MAPERR时,进行的判别,在 fault addr 小于 4K (page_size) 时,会判断可能是 NullPointer问题,

其实在其他地方也有这种判断,比如kernel 中的一些地方,以及 ART里对 Java NullPointerException的判别,都有使用到 fault addr 与 page_size比较,

当 fault addr 小于 4K 时,会被判别为 NullPointer问题。

至于为什么是 4K,还没有弄清除,待调查,有清楚的同事,可以指出。

目前的推测是这样的:

一般根据一个对象,访问其成员时,首先有对象的地址 base,其成员一般是相对 base 有一定的偏移 offset,即 base+offset

那么访问其成员,就是从 base+offset 这内存地址读取数据,一般 offset较小。当是空指针时,即 base 为 0x0,那么 base+offset 的值就是 offset的值,

而一般的 object 不会特别大,成员不会特别多,所以 offset 小于 4K时,就可能是在访问一个 object的成员时,因为 object是 null导致的问题。

那么问题来了,如果一个object大小超过 4K怎么办,访问其最后一个成员时的,offset肯定大于 4K,当 object是 NullPointer时,fault addr肯定大于 4K的 ?【这是一个等待讨论调查的问题1】

2.再来看 backtrace

backtrace:
    #00 pc 00000000  
    #01 pc 72e1ba59  /data/dalvik-cache/arm/system@framework@boot.oat (offset 0x1177000)

先看 frame 0:

pc是 0x0,与fault addr相同,也可以在寄存器信息里看到 pc 00000000;那么这里基本可以这么判断,是从 frame 1进行跳转到 frame 0 时,

跳转的地址是 0x0,从而导致了这个错误。(这里用“基本”的意思,是说不能 100%准确,这个跟 ARM跳转指令相关)

接下来看 frame 1中的代码,是如何跳转到 0x0从而发生错误的。

frame 1:

这个代码是在 boot.oat的 quick code中,运行时的 pc 是 0x72e1ba59; (offset 0x1177000) = oatexec offset + 0x1000

要想看这个代码,如果有 full code dump,直接使用 GDB的 disass命令即可查看,

如果没有coredump或者不是 full dump,其实也还有点过程:

1.需要对该 boot.oat文件进行 oatdump,2.需要根据 pc计算出一个相当于 oatdata位置的 offset,在 oatdump出来的文件中查找就可以了。

具体如何解析这个offset,另一篇其他同事的 wiki里有详细介绍。

Android不同版本之间的计算方式有所不同,可能不太好记住,但只需要理解两点,就比较方便计算了:

1.oatdump中,代码的偏移都是相对于 oatdata位置的

2.tombstone 的backtrace中偏移都是相对于 oatdata -0x1000的

0x1000 oatdata oatexc

接下来继续我们的分析,根据backtrace中 frame1 信息,找到 boot.oat中的代码如下:

      ------------------------------------------------
      0x0141da44: 4607      mov r7, r0 ;chrono
      0x0141da46: 462a      mov r2, r5 ;temporal
      0x0141da48: 4639      mov r1, r7 ;chrono
      0x0141da4a: 6808      ldr r0, [r1] ; class: Chronology的一个实现类
      0x0141da4c: 6fc0      ldr r0, [r0, #124]  ;获取 IMT指针
      0x0141da4e: 6a40      ldr r0, [r0, #36]   ;获取 ArtMethod
      0x0141da50: f8d0e01c  ldr lr, [r0, #28]   ;取得 quick code
      0x0141da54: f2432cc2  mov ip, #12994 ; dex_method_idx
=>    0x0141da58: 47f0      blx lr 
      ------------------------------------------------

也就是说,在 frame1中,运行到 0x0141da58 后,执行 blx lr时,可能要跳转到的 lr是 0x0导致的 FC;

把这段代码对应到 java 代码:

    static ChronoLocalDateTime from(TemporalAccessor temporal) {
        if (temporal instanceof ChronoLocalDateTime) {
            return (ChronoLocalDateTime) temporal;
        }
        Objects.requireNonNull(temporal, "temporal");
        Chronology chrono = temporal.query(TemporalQueries.chronology());
        if (chrono == null) {
            throw new DateTimeException("Unable to obtain ChronoLocalDateTime from TemporalAccessor: " + temporal.getClass());
        }
=>      return chrono.localDateTime(temporal);
    }

上面的 0x0141da58 处跳转,实际对应于 java代码中在调用一个 interface Chronology 的接口函数 localDateTime;

上面的 ARM 指令,已经有所解释,这里说一下 IMT,

IMT,即 Interface Method Table,保存着一个 Class “实现的” 所有接口函数对应的 ArtMethod 指针,线性表:

M1 M2 M3 M4 M5

在 7.0上,这个表是镶嵌在一个对象的 Class所在的内存中的,而在 8.0上,在class中只记录了这个数组的首地址,

数组开辟在另外的内存位置;7.0上,table大小是 64,在 8.0上,table大小是 43,这些数据可能是 google根据经验得来的;

这个 table应该只是为了性能,为了更快的调用接口函数而准备的一个table,用来记录一定数量的接口函数的实现。

那么问题来了,如果接口函数超过这个数量(64/43)后,IMT中存放不完了,怎么办?【这就引出了后面的 IMT Conflict Table】

我们看下这个table中保存的值,这个table里保存的 ArtMethod有两种情况:

1.一种是真正的函数实现,在查找到时,直接使用即可;

2.一种是一个 Runtime Method:

可能是 imt_unimplemented_method_ 或者 imt_conflict_method_ ,(这两个函数是在虚拟机创建的时候生成的)

可能是 运行时新 Resolve Interface 函数后,新创建的 conflict method,

也可能是 quick alloc array 相关的函数实现 art_quick_alloc_array_resolved32_region_tlab;

实际Runtime Method的作用是一个 trampoline,这类函数,会跳转到它记录的 trampoline去;

一个 class的 IMT在 LinkClass 开始的时候,全部初始化为imt_unimplemented_method_,在 LinkClass 的后续过程中,

可能会更改为真正的 interface 函数实现,或者 imt_conflict_method_;

对于 Runtime的成员:imt_unimplemented_method_ 和 imt_conflict_method_,

1.它们对应的 ImtConflictTable表都是空的,这个ImtConflictTable 的结构如下:

conflict M0 implement M0 conflict M1 implement M1 conflict M2 implement M2

这个表里存放的实际上是函数对:{conflict method,implement method}的集合,用来查找 conflict interface method 的真正的实现函数;

2.他们对应的 quick code都是 (boot.oat exec_offset + 0x11 / 0x20(64bit)),而exec_offset + 0x11 / 0x20处的代码是:

ldr x16, [x19, #1184]

br x16

ART quick code中,64bit上,x19代表 ART中的 Thread指针,x19+1184 是 thread→pQuickImtConflictTrampoline,

实际它的值就是art_quick_imt_conflict_trampoline,所以这两种类型的 Runtime Method,最终都会跳转到art_quick_imt_conflict_trampoline;

对于上面讲到的这两种函数,最终跳转到art_quick_imt_conflict_trampoline,先根据 IMT中取出来的 method在 ImtConflictTable中查找,如果找到 implement method,则直接跳转,

如果找不到,则再调用 artInvokeInterfaceTrampoline 去 Resolve对应的 Interface method,Resolve成功后,会做如下事情:

1.如果是从 imt_conflict_method_ 跳转过来的,则会重新创建一个新的 Runtime method,如果是从imt_unimplemented_method_跳转过来的,

则仍然使用从 IMT 取出来的那个函数,我们将这两个函数统称为 new_conflict_method, 这个new_conflict_method 中将记录:

1.1 新创建的一个 ImtConflictTable,其中包含了新 Resolved 的函数对:{new_conflict_method, implement method};

1.2 其 quick code 将直接被设置为 art_quick_imt_conflict_trampoline,不再需要从thread→pQuickImtConflictTrampoline 跳转过来

2.把 new_conflict_method 设置到 Class的 IMT中,替换掉原来的那个 Runtime Method,这样就下次可以根据新设置的这个 new_conflict_method 和其中记录的 ImtConflictTable

来配合,进行查找到 interface 函数的真正的实现实现函数,从而进行跳转即可,不用再次进行 resolve了。

到这里,我们前面的疑问【待调查整理的点2,超过这个数量怎么办 ?】到这里就完成了实现。

interface函数在 IMT表中的 index是怎么计算的:

inline uint32_t ImTable::GetImtIndex(ArtMethod* method) {
  uint32_t class_hash, name_hash, signature_hash;
  GetImtHashComponents(method, &class_hash, &name_hash, &signature_hash);
 
  uint32_t mixed_hash;
  if (!kImTableHashUseCoefficients) {
    mixed_hash = class_hash + name_hash + signature_hash;
  } else {
    mixed_hash = kImTableHashCoefficientClass * class_hash +
                 kImTableHashCoefficientName * name_hash +
                 kImTableHashCoefficientSignature * signature_hash;
  }
 
  return mixed_hash % ImTable::kSize;
}

可以看到,一个interface 函数的最终在 IMT中的 index是通过一个 mixed_hash 取余 43 获取的,而 mixed_hash 由 interface函数的 classs_hash,name_hash,signature_hash共同计算得来;

所以不同 interface的不同函数,经过计算得来的在 IMT中的 index可能是相同的,那么,这就存在了冲突,这也是 ImtConflictTable存在的必要。这种情况是这么来解决的:

当有两个以上 interface函数计算出来的 IMT中的index相同时,IMT中该位置将不再保存任何一个interface函数的实现,而是创建一个 叫做 conflict_method 的函数来处理这种情况,

这个conflict_method 中记录一个上面提到的 ImtConflictTable,以类似键值对的方式存放多个interface method对应的键值对,在使用的时候进行查找。查找方式如下:

这里要结合 java 函数的 quick code 和 art_quick_imt_conflict_trampoline 函数来分析:

------------------------------------------------
0x0141da44: 4607      mov r7, r0 ;chrono
0x0141da46: 462a      mov r2, r5 ;temporal
0x0141da48: 4639      mov r1, r7 ;chrono
0x0141da4a: 6808      ldr r0, [r1] ; class: Chronology
0x0141da4c: 6fc0      ldr r0, [r0, #124]  ;获取 IMT指针
0x0141da4e: 6a40      ldr r0, [r0, #36]   ;获取 ArtMethod
0x0141da50: f8d0e01c  ldr lr, [r0, #28]   ;取得 quick code
0x0141da54: f2432cc2  mov ip, #12994 ; dex_method_idx
0x0141da58: 47f0      blx lr 
------------------------------------------------

可以看到,在 quick code 中,0x0141da4e处的指令,会从 IMT的一个 index处获取一个 ART Method,那么这个 ART Method是一个是什么样的method,我们是不知道的,

1.如果在这个 index 处,没有 interface函数存在冲突,保存的是其对应的真正的 implement method,那么接下来的指令,将会直接跳转到 implement method的 quick code处;

2.如果恰巧这个 index处存在多个 interface函数,表示有冲突,那么此处 Art Method应该是一个 conflict method,且其包含有一个 ConflictImtTable,即用来查找的interface函数真正实现的键值对表,

上面提到过,键值对是这样的{interface_method,implement_method},那么需要根据 interface_method来查找,那就需要先获取 interface method,怎么获取 ?

我们看 quick code的 0x0141da54 处的代码:mov ip, #12994 ;它把 12994给了 ip寄存器,实际在 32bit中,art中的 ip 是 r12。这个 12994是什么?

我们看下oatdump:

1147: Ljava/time/chrono/ChronoLocalDateTime; (offset=0x00017fe4) (type_idx=1206) (StatusInitialized) (OatClassSomeCompiled)
  0: java.time.chrono.ChronoLocalDateTime java.time.chrono.ChronoLocalDateTime.from(java.time.temporal.TemporalAccessor) (dex_method_idx=12781)
    DEX CODE:
      0x0000: 2041 b604                 | instance-of v1, v4, java.time.chrono.ChronoLocalDateTime // type@1206
      0x0002: 3801 0500                 | if-eqz v1, +5
      0x0004: 1f04 b604                 | check-cast v4, java.time.chrono.ChronoLocalDateTime // type@TypeIndex[1206]
      0x0006: 1104                      | return-object v4
      0x0007: 1b01 7e7e 0000            | const-string/jumbo v1, "temporal" // string@32382
      0x000a: 7120 2245 1400            | invoke-static {v4, v1}, java.lang.Object java.util.Objects.requireNonNull(java.lang.Object, java.lang.String) // method@17698
      0x000d: 7100 8737 0000            | invoke-static {}, java.time.temporal.TemporalQuery java.time.temporal.TemporalQueries.chronology() // method@14215
      0x0010: 0c01                      | move-result-object v1
      0x0011: 7220 5637 1400            | invoke-interface {v4, v1}, java.lang.Object java.time.temporal.TemporalAccessor.query(java.time.temporal.TemporalQuery) // method@14166
      0x0014: 0c00                      | move-result-object v0
      0x0015: 1f00 bd04                 | check-cast v0, java.time.chrono.Chronology // type@TypeIndex[1213]
      0x0017: 3900 2000                 | if-nez v0, +32
      0x0019: 2201 9b04                 | new-instance v1, java.time.DateTimeException // type@TypeIndex[1179]
      0x001b: 2202 5d01                 | new-instance v2, java.lang.StringBuilder // type@TypeIndex[349]
      0x001d: 7010 680d 0200            | invoke-direct {v2}, void java.lang.StringBuilder.() // method@3432
      0x0020: 1b03 4f3d 0000            | const-string/jumbo v3, "Unable to obtain ChronoLocalDateTime from TemporalAccessor: " // string@15695
      0x0023: 6e20 840d 3200            | invoke-virtual {v2, v3}, java.lang.StringBuilder java.lang.StringBuilder.append(java.lang.String) // method@3460
      0x0026: 0c02                      | move-result-object v2
      0x0027: 6e10 d00a 0400            | invoke-virtual {v4}, java.lang.Class java.lang.Object.getClass() // method@2768
      0x002a: 0c03                      | move-result-object v3
      0x002b: 6e20 830d 3200            | invoke-virtual {v2, v3}, java.lang.StringBuilder java.lang.StringBuilder.append(java.lang.Object) // method@3459
      0x002e: 0c02                      | move-result-object v2
      0x002f: 6e10 be0d 0200            | invoke-virtual {v2}, java.lang.String java.lang.StringBuilder.toString() // method@3518
      0x0032: 0c02                      | move-result-object v2
      0x0033: 7020 7d2d 2100            | invoke-direct {v1, v2}, void java.time.DateTimeException.(java.lang.String) // method@11645
      0x0036: 2701                      | throw v1
      0x0037: 7220 c232 4000            | invoke-interface {v0, v4}, java.time.chrono.ChronoLocalDateTime java.time.chrono.Chronology.localDateTime(java.time.temporal.TemporalAccessor) // method@12994
      0x003a: 0c01                      | move-result-object v1
      0x003b: 1101                      | return-object v1
 
 
  23: java.time.chrono.ChronoLocalDateTime java.time.chrono.Chronology.localDateTime(java.time.temporal.TemporalAccessor) (dex_method_idx=12994)

上面贴了 java.time.chrono.ChronoLocalDateTime.from 函数的 dex code 和 java.time.chrono.Chronology.localDateTime接口函数的 dex code开头,

可以看到 from()函数的0x37 位置 invoke-interface 传递了一个参数 12994(0x32c2,在dex code中有,在其quick code中直接赋值给 r12),

所以,我们搞明白了,这个12994就是 interface函数的 dex_method_idx,在quick code,从 IMT取出的 ArtMethod,准确跳转到其 quick code之前,

就把 interface 函数的 dex_method_idx保存在了 r12中,如果这个 “从 IMT取出的 ArtMethod”是一个 interface函数的真正的实现函数,那么 r12将用不到了;

但如果像上面描述的,IMT的这个index处存在冲突,那么“从 IMT取出的 ArtMethod” 将是一个 conflict method,其quick code指向 art_quick_imt_conflict_trampoline:

    /*
     * Called to resolve an imt conflict.
     * r0 is the conflict ArtMethod.
     * r12 is a hidden argument that holds the target interface method's dex method index.
     *
     * Note that this stub writes to r0, r4, and r12.
     */
ENTRY art_quick_imt_conflict_trampoline
    ldr r4, [sp, #0]  // Load referrer
    ldr r4, [r4, #ART_METHOD_DEX_CACHE_METHODS_OFFSET_32]   // Load dex cache methods array
    ldr r12, [r4, r12, lsl #POINTER_SIZE_SHIFT]  // Load interface method
    ldr r0, [r0, #ART_METHOD_JNI_OFFSET_32]  // Load ImtConflictTable
    ldr r4, [r0]  // Load first entry in ImtConflictTable.
.Limt_table_iterate:
    cmp r4, r12
    // Branch if found. Benchmarks have shown doing a branch here is better.
    beq .Limt_table_found
    // If the entry is null, the interface method is not in the ImtConflictTable.
    cbz r4, .Lconflict_trampoline
    // Iterate over the entries of the ImtConflictTable.
    ldr r4, [r0, #(2 * __SIZEOF_POINTER__)]!
    b .Limt_table_iterate
.Limt_table_found:
    // We successfully hit an entry in the table. Load the target method
    // and jump to it.
    ldr r0, [r0, #__SIZEOF_POINTER__]
    ldr pc, [r0, #ART_METHOD_QUICK_CODE_OFFSET_32]
.Lconflict_trampoline:
    // Call the runtime stub to populate the ImtConflictTable and jump to the
    // resolved method.
    mov r0, r12  // Load interface method
    INVOKE_TRAMPOLINE_BODY artInvokeInterfaceTrampoline
END art_quick_imt_conflict_trampoline

从这个函数可以看到,r12是一个隐藏的参数,实际上就是上面我们准备的那个 r12,就是将要调用的 interface函数的 dex_method_idx;

而 r0 很显然就是 “从 IMT取出的 ArtMethod” 了,如果跳转到 art_quick_imt_conflict_trampoline 这里,则说明这个“从 IMT取出的 ArtMethod” 是一个 conflict method,

那么它会携带一个 ImtConfictTable,后面会从这里查找。前面的问题还没解答完:{那就需要先获取 interface method,怎么获取 ?}

已经知道 r12保存的是 interface函数的 dex_method_idx,那么就可以依此找到 interface 函数了,每个 dex file都对应一个 dex cache 和一个 dex_cache_resolved_methods_数组,

所以 interface_method =dex_cache_resolved_methods_[dex_method_idx] =dex_cache_resolved_methods_[r12] = dex_cache_resolved_methods_[12994],

这样我们就查找到了 interface_method,就可以从 conflict method中携带的 ImtConflictTable中进行查找该函数的实现了;

查找结果,自然有找到和找不到的情况,找到的情况最好,跳转到 implement_method quick code 去执行就可以了。

如果找不到对应的实现函数,则需要把 interface method 保存到 r0中作为参数,接下来通过 artInvokeInterfaceTrampoline 来继续做一些处理:

.Lconflict_trampoline:
    // Call the runtime stub to populate the ImtConflictTable and jump to the
    // resolved method.
    mov r0, r12  // Load interface method
    INVOKE_TRAMPOLINE_BODY artInvokeInterfaceTrampoline
END art_quick_imt_conflict_trampoline
 
 
.macro INVOKE_TRAMPOLINE_BODY cxx_name
    .extern \cxx_name
    SETUP_SAVE_REFS_AND_ARGS_FRAME r2     @ save callee saves in case allocation triggers GC
    mov    r2, r9                         @ pass Thread::Current
    mov    r3, sp
    bl     \cxx_name                      @ (method_idx, this, Thread*, SP)
    mov    r12, r1                        @ save Method*->code_
    RESTORE_SAVE_REFS_AND_ARGS_FRAME
    cbz    r0, 1f                         @ did we find the target? if not go to exception delivery
    bx     r12                            @ tail call to target
1:
    DELIVER_PENDING_EXCEPTION
.endm

说明一下,在 art_quick_imt_conflict_trampoline 中是通过 INVOKE_TRAMPOLINE_BODY 这宏来跳转到 artInvokeInterfaceTrampoline的,

在跳转之前准备了几个参数:

r0,上面刚提到过,现在保存的是 interface method

r1,一直没变过,还是在 quick code时的 r1,它是 interface的实现类的一个对象

r2,保存当前线程的 Thread 指针

r3,保存当前java 函数的 sp

接下来调用artInvokeInterfaceTrampoline 的目的实际就是应对没找到 interface method实现的情况,就是要进行查找 interface method在 "当前 interface 的实现类" 的接口实现函数。

查找完成将会返回一个TwoWordReturn 的值,分别保存在 r0 和 r1,其中 r0的值表示查找结果,如果找到,r0的值是 1,如果找不到,则值是0;r1表示找到的 implement method 的

quick code位置指针,如果 r0==1,则直接跳转到 r1即可,如果 r0 == 0,则 r1没有意义。

下面继续深入分析artInvokeInterfaceTrampoline:

由于代码较多,只摘出关键流程代码:

extern "C" TwoWordReturn artInvokeInterfaceTrampoline(ArtMethod* interface_method,
                                                      mirror::Object* raw_this_object,
                                                      Thread* self,
                                                      ArtMethod** sp)
    REQUIRES_SHARED(Locks::mutator_lock_) {
 
 
  ObjPtr this_object(raw_this_object);
  Handle cls(hs.NewHandle(this_object->GetClass()));
  ArtMethod* caller_method = QuickArgumentVisitor::GetCallingMethod(sp);
  ArtMethod* method = nullptr;
  ImTable* imt = cls->GetImt(kRuntimePointerSize);
 
 
  if (LIKELY(interface_method->GetDexMethodIndex() != DexFile::kDexNoIndex)) {
    // TODO: 说明代码
    如果 dex_cache_resolved_methods_ 数组中查到的 interface函数已经被 Resolved,
    那么就根据这个 interface method 到当前 object对应 class的 iftable中去查找这个 interface method的实现;
  } else {
    // TODO: 说明代码
    如果 dex_cache_resolved_methods_ 数组中查到的 interface函数没有被 Resolved,
    则先从 caller method 的 dex code中获取想要调用的interface method 的 dex_method_idx,
    根据这个 dex_method_idx 从 dexfile 中获取对应的interface method, 获取后插入到 dex_cache_resolved_methods_ 数组中,
    然后再使用这个 interface method ,从 当前 object对应class的 iftable中查找这个 interface method的实现;
  }
 
 
  // 下面这段代码的逻辑其实在前面已经讲过了,可以对照说明看一下
  // 调用 artInvokeInterfaceTrampoline 去 Resolve对应的 Interface method,Resolve成功后,会做如下事情:
  // 1.如果是从 imt_conflict_method_ 跳转过来的,则会重新创建一个新的 Runtime method,如果是从 imt_unimplemented_method_跳转过来的,
  //   则仍然使用从IMT取出来的那个函数,我们将这两个函数统称为 new_conflict_method, 这个new_conflict_method 中将记录:
  //    1.1 新创建的一个 ImtConflictTable,其中包含了新 Resolved 的函数对:{new_conflict_method, implement method};
  //    1.2 其 quick code 将直接被设置为 art_quick_imt_conflict_trampoline,不再需要从 thread→pQuickImtConflictTrampoline 跳转过来
  // 2.把 new_conflict_method 设置到 Class的 IMT中,替换掉原来的那个 Runtime Method,这样就下次可以根据新设置的这个 new_conflict_method 和
  //   其中记录的 ImtConflictTable来配合,进行查找到 interface 函数的真正的实现实现函数,从而进行跳转即可,不用再次进行 resolve了。
 
 
  uint32_t imt_index = ImTable::GetImtIndex(interface_method);
  ArtMethod* conflict_method = imt->Get(imt_index, kRuntimePointerSize);
  if (conflict_method->IsRuntimeMethod()) {
    ArtMethod* new_conflict_method = Runtime::Current()->GetClassLinker()->AddMethodToConflictTable(
        cls.Get(),
        conflict_method,
        interface_method,
        method,
        /*force_new_conflict_method*/false);
    if (new_conflict_method != conflict_method) {
      // Update the IMT if we create a new conflict method. No fence needed here, as the
      // data is consistent.
      imt->Set(imt_index,
               new_conflict_method,
               kRuntimePointerSize);
    }
 
 
  const void* code = method->GetEntryPointFromQuickCompiledCode();
  return GetTwoWordSuccessValue(reinterpret_cast(code),
                                reinterpret_cast(method));
}
下面看看这个过程中访问到的几个表结构:
\

3.使用 64 bit core来分析

根据上面这些表结构,我们从 core dump中,还原调用 interface函数的 FC流程:

1.首先,在 java 函数的quick code中,先获取当前对象的 class的 IMT中保存的在 IMT中对应当前想调用的interface method:

由于32bit 时不是第一现场,所以先用 64 bit 的core 来分析:
      ------------------------------------------------
      0x005b5f00: aa0003f6  mov x22, x0
      0x005b5f04: aa1403e2  mov x2, x20
      0x005b5f08: aa1603e1  mov x1, x22
      0x005b5f0c: d2865851  mov x17, #0x32c2   ;dex_method_idx
      0x005b5f10: b9400020  ldr w0, [x1]       ;获取当前 object 的 class
      0x005b5f14: f9404000  ldr x0, [x0, #128] ;Imt 指针
      0x005b5f18: f9402400  ldr x0, [x0, #72]  ;Imt表中index为 [72/8=9]的 ArtMethod
      0x005b5f1c: f940141e  ldr lr, [x0, #40]  ;获取 quick code
      0x005b5f20: d63f03c0  blr lr
      ------------------------------------------------

当前 x1的值是314593768 (想要获取这个地址,可以打 log,也可以断点断到代码 0x0141da4a 这里):

意思当前的实现 Chronology接口的对象为314593768

(gdb) x 314593768
0x12c051e8: 0x12e43690

这个对象真实的 class是:

(gdb) class_print_name 0x12e43690
java.time.chrono.HijrahChronology [Normal]

接下来 class 偏移 128获取 当前 Class 的 IMT指针 :

(gdb) x /gx 0x12e43690+128
0x12e43710: 0x000000768d242f7

IMT函数就是这么一步步获取的,由于写了dump脚本,直接贴出当前class的 IMT :

(gdb) class_print_imt 0x12e43690
 
CLASS NAME:   java.time.chrono.HijrahChronology   [Normal]
$1 = 0x768d242f70
     ArtMethod[00][0x006fecf4b0]:  dex_method_idx[0xffffffff], imt_unimplemented_method_
     ArtMethod[01][0x006fecf4b0]:  dex_method_idx[0xffffffff], imt_unimplemented_method_
     ArtMethod[02][0x006fecf4b0]:  dex_method_idx[0xffffffff], imt_unimplemented_method_
     ArtMethod[03][0x768d242430]:  dex_method_idx[13071], m_idx[15], java.time.chrono.HijrahChronology.resolveDate "(Ljava/util/Map;Ljava/time/format/ResolverStyle;)
     ArtMethod[04][0x006fecf4b0]:  dex_method_idx[0xffffffff], imt_unimplemented_method_
     ArtMethod[05][0x006fecf4b0]:  dex_method_idx[0xffffffff], imt_unimplemented_method_
     ArtMethod[06][0x006fecf480]:  dex_method_idx[0xffffffff], imt_conflict_method_
     ArtMethod[07][0x768d242070]:  dex_method_idx[13044], m_idx[40], java.time.chrono.HijrahChronology.eraOf "(I)
     ArtMethod[08][0x006fecf4b0]:  dex_method_idx[0xffffffff], imt_unimplemented_method_
     ArtMethod[09][0x006fecf480]:  dex_method_idx[0xffffffff], imt_conflict_method_
     ArtMethod[10][0x006fecf4b0]:  dex_method_idx[0xffffffff], imt_unimplemented_method_
     ArtMethod[11][0x768d242370]:  dex_method_idx[13060], m_idx[44], java.time.chrono.HijrahChronology.isLeapYear "(J)
     ArtMethod[12][0x006fecf480]:  dex_method_idx[0xffffffff], imt_conflict_method_
     ArtMethod[13][0x006fecf4b0]:  dex_method_idx[0xffffffff], imt_unimplemented_method_
     ArtMethod[14][0x006fecf4b0]:  dex_method_idx[0xffffffff], imt_unimplemented_method_
     ArtMethod[15][0x768d2428e0]:  dex_method_idx[12669], m_idx[04], java.time.chrono.AbstractChronology.hashCode "()
     ArtMethod[16][0x768d241d70]:  dex_method_idx[13022], m_idx[37], java.time.chrono.HijrahChronology.date "(Ljava/time/temporal/TemporalAccessor;)
     ArtMethod[17][0x006fecf4b0]:  dex_method_idx[0xffffffff], imt_unimplemented_method_
     ArtMethod[18][0x768d241e30]:  dex_method_idx[13026], m_idx[38], java.time.chrono.HijrahChronology.dateEpochDay "(J)
     ArtMethod[19][0x768d2424f0]:  dex_method_idx[13077], m_idx[35], java.time.chrono.HijrahChronology.zonedDateTime "(Ljava/time/temporal/TemporalAccessor;)
     ArtMethod[20][0x006fecf480]:  dex_method_idx[0xffffffff], imt_conflict_method_
     ArtMethod[21][0x768d2428b0]:  dex_method_idx[12663], m_idx[01], java.time.chrono.AbstractChronology.equals "(Ljava/lang/Object;)
     ArtMethod[22][0x768d2420d0]:  dex_method_idx[13046], m_idx[41], java.time.chrono.HijrahChronology.eras "()
     ArtMethod[23][0x768d2421c0]:  dex_method_idx[13051], m_idx[43], java.time.chrono.HijrahChronology.getId "()
     ArtMethod[24][0x006fecf4b0]:  dex_method_idx[0xffffffff], imt_unimplemented_method_
     ArtMethod[25][0x768d241ef0]:  dex_method_idx[13030], m_idx[29], java.time.chrono.HijrahChronology.dateNow "(Ljava/time/ZoneId;)
     ArtMethod[26][0x006fecf4b0]:  dex_method_idx[0xffffffff], imt_unimplemented_method_
     ArtMethod[27][0x768d2423d0]:  dex_method_idx[13066], m_idx[45], java.time.chrono.HijrahChronology.prolepticYear "(Ljava/time/chrono/Era;I)
     ArtMethod[28][0x768d241fe0]:  dex_method_idx[13035], m_idx[30], java.time.chrono.HijrahChronology.dateYearDay "(Ljava/time/chrono/Era;II)
     ArtMethod[29][0x006fecf4b0]:  dex_method_idx[0xffffffff], imt_unimplemented_method_
     ArtMethod[30][0x7681740338]:  dex_method_idx[0xffffffff]
     ArtMethod[31][0x768d241d10]:  dex_method_idx[13020], m_idx[36], java.time.chrono.HijrahChronology.date "(III)
     ArtMethod[32][0x006fecf4b0]:  dex_method_idx[0xffffffff], imt_unimplemented_method_
     ArtMethod[33][0x006fecf4b0]:  dex_method_idx[0xffffffff], imt_unimplemented_method_
     ArtMethod[34][0x006fecf4b0]:  dex_method_idx[0xffffffff], imt_unimplemented_method_
     ArtMethod[35][0x006fecf4b0]:  dex_method_idx[0xffffffff], imt_unimplemented_method_
     ArtMethod[36][0x006fecf480]:  dex_method_idx[0xffffffff], imt_conflict_method_
     ArtMethod[37][0x006fecf4b0]:  dex_method_idx[0xffffffff], imt_unimplemented_method_
     ArtMethod[38][0x006fecf4b0]:  dex_method_idx[0xffffffff], imt_unimplemented_method_
     ArtMethod[39][0x768d242850]:  dex_method_idx[12657], m_idx[12], java.time.chrono.AbstractChronology.compareTo "(Ljava/lang/Object;)
     ArtMethod[40][0x006fecf4b0]:  dex_method_idx[0xffffffff], imt_unimplemented_method_
     ArtMethod[41][0x006fecf4b0]:  dex_method_idx[0xffffffff], imt_unimplemented_method_
     ArtMethod[42][0x768d242e80]:  dex_method_idx[12990], m_idx[31], java.time.chrono.Chronology.getDisplayName "(Ljava/time/format/TextStyle;Ljava/util/Locale;)

由于要获取的是 IMT中 index为 9 的 ArtMethod,所以是 

ArtMethod[09][0x006fecf480]:  dex_method_idx[0xffffffff], imt_conflict_method_

从这里,我们已经能够看到当前 index 在 IMT 中保存的 ArtMethod 是一个 Conflict method,说明有冲突,即有多个 interface method都保存在 IMT的这个index上;

详细看一下这个ArtMethod:

(gdb) method_print_all 0x006fecf480
This is a Runtime Method!
art::Runtime::instance_->imt_conflict_method_
$2 = {
  static kCheckDeclaringClassState = false,
  static kRuntimeMethodDexMethodIndex = 4294967295,
  declaring_class_ = {
    root_ = {
      > = {
        reference_ = 0
      }, }
  },
  access_flags_ = {
    > = {
      > = {
        __a_ = 0
      }, }, },
  dex_code_item_offset_ = 0,
  dex_method_index_ = 4294967295,
  method_index_ = 0,
  hotness_count_ = 0,
  ptr_sized_fields_ = {
    dex_cache_resolved_methods_ = 0x0,
    data_ = 0x6fecf430,
    entry_point_from_quick_compiled_code_ = 0x70e5f020
  }
}
我们可以看下 data_ (此时存放的是 IMT Conflict Table的指针)和quick code:

(gdb) p *('art::ImtConflictTable'*)0x6fecf430
$3 = {
  {
    data32_ = 0x6fecf430,
    data64_ = 0x6fecf430
  }
}
(gdb) x /4x 0x6fecf430
0x6fecf430: 0x0000000000000000  0x0000000000000000
0x6fecf440: 0x0000000000000000  0x0000000000000000
 
(gdb) disassemble 0x70e5f020,+0x10
Dump of assembler code from 0x70e5f020 to 0x70e5f030:
   0x0000000070e5f020:  ldr x16, [x19,#1184]
   0x0000000070e5f024:  br  x16
   0x0000000070e5f028:  .inst   0x00000000 ; undefined
   0x0000000070e5f02c:  .inst   0x00000000 ; undefined
End of assembler dump.

可以看到 这个 ArtMethod 就是 art::Runtime::instance_->imt_conflict_method_,

且method数据与前面分析契合,ImtConflict Table是空的, quick code指向的代码也是将要跳转到 art_quick_imt_conflict_trampoline函数;

2.分析跳转到art_quick_imt_conflict_trampoline后的执行:
/*
     * Called to resolve an imt conflict.
     * x0 is the conflict ArtMethod.
     * xIP1 is a hidden argument that holds the target interface method's dex method index.
     *
     * Note that this stub writes to xIP0, xIP1, and x0.
     */
    .extern artInvokeInterfaceTrampoline
ENTRY art_quick_imt_conflict_trampoline
    ldr xIP0, [sp, #0]  // Load referrer
    ldr xIP0, [xIP0, #ART_METHOD_DEX_CACHE_METHODS_OFFSET_64]   // Load dex cache methods array
    ldr xIP0, [xIP0, xIP1, lsl #POINTER_SIZE_SHIFT]  // Load interface method
    ldr xIP1, [x0, #ART_METHOD_JNI_OFFSET_64]  // Load ImtConflictTable
    ldr x0, [xIP1]  // Load first entry in ImtConflictTable.
.Limt_table_iterate:
    cmp x0, xIP0
    // Branch if found. Benchmarks have shown doing a branch here is better.
    beq .Limt_table_found
    // If the entry is null, the interface method is not in the ImtConflictTable.
    cbz x0, .Lconflict_trampoline
    // Iterate over the entries of the ImtConflictTable.
    ldr x0, [xIP1, #(2 * __SIZEOF_POINTER__)]!
    b .Limt_table_iterate
.Limt_table_found:
    // We successfully hit an entry in the table. Load the target method
    // and jump to it.
    ldr x0, [xIP1, #__SIZEOF_POINTER__]
    ldr xIP0, [x0, #ART_METHOD_QUICK_CODE_OFFSET_64]
    br xIP0
.Lconflict_trampoline:
    // Call the runtime stub to populate the ImtConflictTable and jump to the
    // resolved method.
    mov x0, xIP0  // Load interface method
    INVOKE_TRAMPOLINE_BODY artInvokeInterfaceTrampoline
END art_quick_imt_conflict_trampoline

这个函数:

1.从 caller method 保存的 dex_cache_resolved_methods_ 中获取 interface method

2.从 Imt 中获取的 ArtMethod中保存的 ImtConflictTable 中查询当前 conflict interface method是否已经 resolved

3.如果查到,直接跳转到 quick code;如果没查到,则跳转到 artInvokeInterfaceTrampoline 去 处理

从 coredump 中看下:

dex_cache_resolved_methods_ 可以根据代码,使用 caller method获取,也可以使用已知的 art_method获取,
这里使用已知的 art_method:
(gdb) method_print_all 0x768d242430
 dex_method_idx[13071], m_idx[15], java.time.chrono.HijrahChronology.resolveDate "(Ljava/util/Map;Ljava/time/format/ResolverStyle;)
$5 = {
  static kCheckDeclaringClassState = false,
  static kRuntimeMethodDexMethodIndex = 4294967295,
  declaring_class_ = {
    root_ = {
      > = {
        reference_ = 316946064
      }, }
  },
  access_flags_ = {
    > = {
      > = {
        __a_ = 528449
      }, }, },
  dex_code_item_offset_ = 1576576,
  dex_method_index_ = 13071,
  method_index_ = 15,
  hotness_count_ = 0,
  ptr_sized_fields_ = {
    dex_cache_resolved_methods_ = 0x700f8710,
    data_ = 0x0,
    entry_point_from_quick_compiled_code_ = 0x714d8fe0
  }
}
 
所以  dex_cache_resolved_methods_ = 0x700f8710;
那么 interface method 是:
(gdb) x /gx 0x700f8710+12994*8
0x70111d20: 0x000000768d242eb0
 
 
(gdb) method_print_all 0x000000768d242eb0
 dex_method_idx[12994], m_idx[32], java.time.chrono.Chronology.localDateTime "(Ljava/time/temporal/TemporalAccessor;)
$6 = {
  static kCheckDeclaringClassState = false,
  static kRuntimeMethodDexMethodIndex = 4294967295,
  declaring_class_ = {
    root_ = {
      > = {
        reference_ = 316945944
      }, }
  },
  access_flags_ = {
    > = {
      > = {
        __a_ = 5767169
      }, }, },
  dex_code_item_offset_ = 1552148,
  dex_method_index_ = 12994,
  method_index_ = 32,
  hotness_count_ = 0,
  ptr_sized_fields_ = {
    dex_cache_resolved_methods_ = 0x700f8710,
    data_ = 0x0,
    entry_point_from_quick_compiled_code_ = 0x714b8130
  }
}

这里需要关注的是:

method 确实是 interface Chronology的 localDateTime函数,且 dex_method_index_ = 12994,method_index_ = 32;

dex_cache_resolved_methods_ 也与上面一个正常的 method中保存的一致;

quick code 是 0x714b8130,经过计算也匹配。

23: java.time.chrono.ChronoLocalDateTime java.time.chrono.Chronology.localDateTime(java.time.temporal.TemporalAccessor) (dex_method_idx=12994)

CODE: (code_offset=0x00621130 size_offset=0x0062112c size=580)...

00000000'70e96000-00000000'71179fff r-- 0 2e4000 /system/framework/arm64/boot-core-oj.oat (BuildId: ae95f1758bdccdd36fb66fb0072286557c4abe0e)

(gdb) p /x 0x70e96000+0x1000+0x00621130
$8 = 0x714b8130

好像数据都是对的。

3.那么接下来我们继续看artInvokeInterfaceTrampoline(因为 ImtConflictTable 是空的,所以肯定要进入这个函数的)

这个函数前面已经分析过,会根据当前 interface method是否被 Resolved区分处理,

从上面的数据可以看到,当前 interface method已经被 resolved的;且由于其对应的 ImtConflictTable当前还是空的,

所以同尝试从当前对象 class的 iftable 中查找:

查找的方式是,使用 interface method 的 method_index (前面已经看到,是32)作为 iftable的 method_array的index, 在其 iftable中查找:

(gdb) class_print_iftable 0x12e43690
 
CLASS NAME:   java.time.chrono.HijrahChronology   [Normal]
 
Interface[0x6ff48628]: java.lang.Comparable   [Abstract]   [Interface]        *('art::mirror::PointerArray'*)(0x12f10820)
     first0: 0x12f1082c
     first1: 0x12f10830
     ArtMethod[00][0x768d242850]:  dex_method_idx[12657], m_idx[12], java.time.chrono.AbstractChronology.compareTo "(Ljava/lang/Object;)
 
Interface[0x12e43618]: java.time.chrono.Chronology   [Abstract]   [Interface]        *('art::mirror::PointerArray'*)(0x12f10838)
     first0: 0x12f10844
     first1: 0x12f10848
     ArtMethod[00][0x768d242850]:  dex_method_idx[12657], m_idx[12], java.time.chrono.AbstractChronology.compareTo "(Ljava/lang/Object;)
     ArtMethod[01][0x768d242880]:  dex_method_idx[12658], m_idx[13], java.time.chrono.AbstractChronology.compareTo "(Ljava/time/chrono/Chronology;)
     ArtMethod[02][0x768d241d10]:  dex_method_idx[13020], m_idx[36], java.time.chrono.HijrahChronology.date "(III)
     ArtMethod[03][0x768d241d40]:  dex_method_idx[13021], m_idx[26], java.time.chrono.HijrahChronology.date "(Ljava/time/chrono/Era;III)
     ArtMethod[04][0x768d241d70]:  dex_method_idx[13022], m_idx[37], java.time.chrono.HijrahChronology.date "(Ljava/time/temporal/TemporalAccessor;)
     ArtMethod[05][0x768d241e30]:  dex_method_idx[13026], m_idx[38], java.time.chrono.HijrahChronology.dateEpochDay "(J)
     ArtMethod[06][0x768d241e90]:  dex_method_idx[13028], m_idx[27], java.time.chrono.HijrahChronology.dateNow "()
     ArtMethod[07][0x768d241ec0]:  dex_method_idx[13029], m_idx[28], java.time.chrono.HijrahChronology.dateNow "(Ljava/time/Clock;)
     ArtMethod[08][0x768d241ef0]:  dex_method_idx[13030], m_idx[29], java.time.chrono.HijrahChronology.dateNow "(Ljava/time/ZoneId;)
     ArtMethod[09][0x768d241fb0]:  dex_method_idx[13034], m_idx[39], java.time.chrono.HijrahChronology.dateYearDay "(II)
     ArtMethod[10][0x768d241fe0]:  dex_method_idx[13035], m_idx[30], java.time.chrono.HijrahChronology.dateYearDay "(Ljava/time/chrono/Era;II)
     ArtMethod[11][0x768d2428b0]:  dex_method_idx[12663], m_idx[01], java.time.chrono.AbstractChronology.equals "(Ljava/lang/Object;)
     ArtMethod[12][0x768d242070]:  dex_method_idx[13044], m_idx[40], java.time.chrono.HijrahChronology.eraOf "(I)
     ArtMethod[13][0x768d2420d0]:  dex_method_idx[13046], m_idx[41], java.time.chrono.HijrahChronology.eras "()
     ArtMethod[14][0x768d242100]:  dex_method_idx[13047], m_idx[42], java.time.chrono.HijrahChronology.getCalendarType "()
     ArtMethod[15][0x768d242e80]:  dex_method_idx[12990], m_idx[31], java.time.chrono.Chronology.getDisplayName "(Ljava/time/format/TextStyle;Ljava/util/Locale;)
     ArtMethod[16][0x768d2421c0]:  dex_method_idx[13051], m_idx[43], java.time.chrono.HijrahChronology.getId "()
     ArtMethod[17][0x768d2428e0]:  dex_method_idx[12669], m_idx[04], java.time.chrono.AbstractChronology.hashCode "()
     ArtMethod[18][0x768d242370]:  dex_method_idx[13060], m_idx[44], java.time.chrono.HijrahChronology.isLeapYear "(J)
     ArtMethod[19][0x768d2423a0]:  dex_method_idx[13062], m_idx[32], java.time.chrono.HijrahChronology.localDateTime "(Ljava/time/temporal/TemporalAccessor;)
     ArtMethod[20][0x768d242ee0]:  dex_method_idx[12997], m_idx[33], java.time.chrono.Chronology.period "(III)
     ArtMethod[21][0x768d2423d0]:  dex_method_idx[13066], m_idx[45], java.time.chrono.HijrahChronology.prolepticYear "(Ljava/time/chrono/Era;I)
     ArtMethod[22][0x768d242400]:  dex_method_idx[13067], m_idx[46], java.time.chrono.HijrahChronology.range "(Ljava/time/temporal/ChronoField;)
     ArtMethod[23][0x768d242430]:  dex_method_idx[13071], m_idx[15], java.time.chrono.HijrahChronology.resolveDate "(Ljava/util/Map;Ljava/time/format/ResolverStyle;)
     ArtMethod[24][0x768d242af0]:  dex_method_idx[12694], m_idx[07], java.time.chrono.AbstractChronology.toString "()
     ArtMethod[25][0x768d2424c0]:  dex_method_idx[13076], m_idx[34], java.time.chrono.HijrahChronology.zonedDateTime "(Ljava/time/Instant;Ljava/time/ZoneId;)
     ArtMethod[26][0x768d2424f0]:  dex_method_idx[13077], m_idx[35], java.time.chrono.HijrahChronology.zonedDateTime "(Ljava/time/temporal/TemporalAccessor;)
 
Interface[0x6ff77120]: java.io.Serializable   [Abstract]   [Interface]        *('art::mirror::PointerArray'*)(0x0)
     This Interface have no method.

我们可以看到,对应 interface Chronology 的 method array总共有 27个函数,而现在要查找的 index是 32,应该会得到一个不可预期的值;

按理讲,这个函数的 index应该会小于27的,为何是 32呢 ? 接下来检查下 interface的 virtual method:


(gdb) class_print_methods_ 0x12e43618
 
CLASS NAME:   java.time.chrono.Chronology   [Abstract]   [Interface]
     ArtMethod[00][0x768d23c628]:  dex_method_idx[12987], m_idx[00], java.time.chrono.Chronology.from "(Ljava/time/temporal/TemporalAccessor;)
     ArtMethod[01][0x768d23c658]:  dex_method_idx[12988], m_idx[01], java.time.chrono.Chronology.getAvailableChronologies "()
     ArtMethod[02][0x768d23c688]:  dex_method_idx[12995], m_idx[02], java.time.chrono.Chronology.of "(Ljava/lang/String;)
     ArtMethod[03][0x768d23c6b8]:  dex_method_idx[12996], m_idx[03], java.time.chrono.Chronology.ofLocale "(Ljava/util/Locale;)
 
     ArtMethod[04][0x768d23c6e8]:  dex_method_idx[12973], m_idx[00], java.time.chrono.Chronology.compareTo "(Ljava/lang/Object;)
     ArtMethod[05][0x768d23c718]:  dex_method_idx[12974], m_idx[01], java.time.chrono.Chronology.compareTo "(Ljava/time/chrono/Chronology;)
     ArtMethod[06][0x768d23c748]:  dex_method_idx[12975], m_idx[02], java.time.chrono.Chronology.date "(III)
     ArtMethod[07][0x768d23c778]:  dex_method_idx[12976], m_idx[03], java.time.chrono.Chronology.date "(Ljava/time/chrono/Era;III)
     ArtMethod[08][0x768d23c7a8]:  dex_method_idx[12977], m_idx[04], java.time.chrono.Chronology.date "(Ljava/time/temporal/TemporalAccessor;)
     ArtMethod[09][0x768d23c7d8]:  dex_method_idx[12978], m_idx[05], java.time.chrono.Chronology.dateEpochDay "(J)
     ArtMethod[10][0x768d23c808]:  dex_method_idx[12979], m_idx[06], java.time.chrono.Chronology.dateNow "()
     ArtMethod[11][0x768d23c838]:  dex_method_idx[12980], m_idx[07], java.time.chrono.Chronology.dateNow "(Ljava/time/Clock;)
     ArtMethod[12][0x768d23c868]:  dex_method_idx[12981], m_idx[08], java.time.chrono.Chronology.dateNow "(Ljava/time/ZoneId;)
     ArtMethod[13][0x768d23c898]:  dex_method_idx[12982], m_idx[09], java.time.chrono.Chronology.dateYearDay "(II)
     ArtMethod[14][0x768d23c8c8]:  dex_method_idx[12983], m_idx[10], java.time.chrono.Chronology.dateYearDay "(Ljava/time/chrono/Era;II)
     ArtMethod[15][0x768d23c8f8]:  dex_method_idx[12984], m_idx[11], java.time.chrono.Chronology.equals "(Ljava/lang/Object;)
     ArtMethod[16][0x768d23c928]:  dex_method_idx[12985], m_idx[12], java.time.chrono.Chronology.eraOf "(I)
     ArtMethod[17][0x768d23c958]:  dex_method_idx[12986], m_idx[13], java.time.chrono.Chronology.eras "()
     ArtMethod[18][0x768d23c988]:  dex_method_idx[12989], m_idx[14], java.time.chrono.Chronology.getCalendarType "()
     ArtMethod[19][0x768d23c9b8]:  dex_method_idx[12990], m_idx[15], java.time.chrono.Chronology.getDisplayName "(Ljava/time/format/TextStyle;Ljava/util/Locale;)
     ArtMethod[20][0x768d23c9e8]:  dex_method_idx[12991], m_idx[16], java.time.chrono.Chronology.getId "()
     ArtMethod[21][0x768d23ca18]:  dex_method_idx[12992], m_idx[17], java.time.chrono.Chronology.hashCode "()
     ArtMethod[22][0x768d23ca48]:  dex_method_idx[12993], m_idx[18], java.time.chrono.Chronology.isLeapYear "(J)
     ArtMethod[23][0x768d23ca78]:  dex_method_idx[12994], m_idx[19], java.time.chrono.Chronology.localDateTime "(Ljava/time/temporal/TemporalAccessor;)
     ArtMethod[24][0x768d23caa8]:  dex_method_idx[12997], m_idx[20], java.time.chrono.Chronology.period "(III)
     ArtMethod[25][0x768d23cad8]:  dex_method_idx[12998], m_idx[21], java.time.chrono.Chronology.prolepticYear "(Ljava/time/chrono/Era;I)
     ArtMethod[26][0x768d23cb08]:  dex_method_idx[12999], m_idx[22], java.time.chrono.Chronology.range "(Ljava/time/temporal/ChronoField;)
     ArtMethod[27][0x768d23cb38]:  dex_method_idx[13000], m_idx[23], java.time.chrono.Chronology.resolveDate "(Ljava/util/Map;Ljava/time/format/ResolverStyle;)
     ArtMethod[28][0x768d23cb68]:  dex_method_idx[13001], m_idx[24], java.time.chrono.Chronology.toString "()
     ArtMethod[29][0x768d23cb98]:  dex_method_idx[13002], m_idx[25], java.time.chrono.Chronology.zonedDateTime "(Ljava/time/Instant;Ljava/time/ZoneId;)
     ArtMethod[30][0x768d23cbc8]:  dex_method_idx[13003], m_idx[26], java.time.chrono.Chronology.zonedDateTime "(Ljava/time/temporal/TemporalAccessor;)

interface类型的class,其函数都在 class→methods_数组中,在这个数组中,直接存储的method,而不是method指针;

另外,其保存了一个class的所有函数,且是分块的:

direct methods virtual methods copied methods

所以,我们看到的 interface Chronology最开始的 4个函数是 direct method,后面的 27个函数是 virtual method。

我们看到,java.time.chrono.Chronology.localDateTime 这个 virtual method的 method id是 19,

再看下 HijrahChronology类的 iftable中, index=19处也是localDateTime这个函数,那么看起来 19才是对的,才能匹配上,

如果 dex_cache_resolved_methods_中,保存的那个 ArtMethod的 method_index是 19 的话,应该就不会发生问题了。

那么我们就要看下这个 32是从哪里来的 ?

我们看下 HijrahChronology 的 iftable的Chronology对应的 index=19处的localDateTime:

     ArtMethod[19][0x768d2423a0]:  dex_method_idx[13062], m_idx[32], java.time.chrono.HijrahChronology.localDateTime "(Ljava/time/temporal/TemporalAccessor;)

这个函数的 method index是32,那么会不会有关系?

另外,我们知道,当一个类继承另一个类的时候,会把父类的 virtual methods拷贝过来,且保持原有的 method index,

如果有override,那么把override的新函数,填到 virtual table中,但 method index仍沿用父类的 method_index;

但是我们确认,这个函数应该不是导致 dex_cache_resolved_methods_[12994] 处 ArtMethod index是32的原因,因为这个函数的 dex_method_index是 13062,并不是 12994;

现在看下HijrahChronology的 virtual methods:

(gdb) class_print_vtable 0x12e43690
 
CLASS NAME:   java.time.chrono.HijrahChronology   [Normal]
This Class have no vtable, print EmbeddedVtable[72]:
$9 = 0x12e43718
     ArtMethod[00][0x0070095660]:  dex_method_idx[02765], m_idx[00], java.lang.Object ??o.clone "()
     ArtMethod[01][0x768d2428b0]:  dex_method_idx[12663], m_idx[01], java.time.chrono.AbstractChronology.equals "(Ljava/lang/Object;)
     ArtMethod[02][0x00700956c0]:  dex_method_idx[02767], m_idx[02], java.lang.Object ??o.finalize "()
     ArtMethod[03][0x00700956f0]:  dex_method_idx[02768], m_idx[03], java.lang.Object ??o.getClass "()
     ArtMethod[04][0x768d2428e0]:  dex_method_idx[12669], m_idx[04], java.time.chrono.AbstractChronology.hashCode "()
     ArtMethod[05][0x0070095750]:  dex_method_idx[02773], m_idx[05], java.lang.Object ??o.notify "()
     ArtMethod[06][0x0070095780]:  dex_method_idx[02774], m_idx[06], java.lang.Object ??o.notifyAll "()
     ArtMethod[07][0x768d242af0]:  dex_method_idx[12694], m_idx[07], java.time.chrono.AbstractChronology.toString "()
     ArtMethod[08][0x00700957e0]:  dex_method_idx[02776], m_idx[08], java.lang.Object ??o.wait "()
     ArtMethod[09][0x0070095810]:  dex_method_idx[02777], m_idx[09], java.lang.Object ??o.wait "(J)
     ArtMethod[10][0x0070095840]:  dex_method_idx[02778], m_idx[10], java.lang.Object ??o.wait "(JI)
     ArtMethod[11][0x768d242820]:  dex_method_idx[12656], m_idx[11], java.time.chrono.AbstractChronology.addFieldValue "(Ljava/util/Map;Ljava/time/temporal/ChronoField;J)
     ArtMethod[12][0x768d242850]:  dex_method_idx[12657], m_idx[12], java.time.chrono.AbstractChronology.compareTo "(Ljava/lang/Object;)
     ArtMethod[13][0x768d242880]:  dex_method_idx[12658], m_idx[13], java.time.chrono.AbstractChronology.compareTo "(Ljava/time/chrono/Chronology;)
     ArtMethod[14][0x768d242910]:  dex_method_idx[12684], m_idx[14], java.time.chrono.AbstractChronology.resolveAligned "(Ljava/time/chrono/ChronoLocalDate;JJJ)
     ArtMethod[15][0x768d242430]:  dex_method_idx[13071], m_idx[15], java.time.chrono.HijrahChronology.resolveDate "(Ljava/util/Map;Ljava/time/format/ResolverStyle;)
     ArtMethod[16][0x768d242970]:  dex_method_idx[12686], m_idx[16], java.time.chrono.AbstractChronology.resolveProlepticMonth "(Ljava/util/Map;Ljava/time/format/ResolverStyle;)
     ArtMethod[17][0x768d2429a0]:  dex_method_idx[12687], m_idx[17], java.time.chrono.AbstractChronology.resolveYAA "(Ljava/util/Map;Ljava/time/format/ResolverStyle;)
     ArtMethod[18][0x768d2429d0]:  dex_method_idx[12688], m_idx[18], java.time.chrono.AbstractChronology.resolveYAD "(Ljava/util/Map;Ljava/time/format/ResolverStyle;)
     ArtMethod[19][0x768d242a00]:  dex_method_idx[12689], m_idx[19], java.time.chrono.AbstractChronology.resolveYD "(Ljava/util/Map;Ljava/time/format/ResolverStyle;)
     ArtMethod[20][0x768d242a30]:  dex_method_idx[12690], m_idx[20], java.time.chrono.AbstractChronology.resolveYMAA "(Ljava/util/Map;Ljava/time/format/ResolverStyle;)
     ArtMethod[21][0x768d242a60]:  dex_method_idx[12691], m_idx[21], java.time.chrono.AbstractChronology.resolveYMAD "(Ljava/util/Map;Ljava/time/format/ResolverStyle;)
     ArtMethod[22][0x768d242a90]:  dex_method_idx[12692], m_idx[22], java.time.chrono.AbstractChronology.resolveYMD "(Ljava/util/Map;Ljava/time/format/ResolverStyle;)
     ArtMethod[23][0x768d242ac0]:  dex_method_idx[12693], m_idx[23], java.time.chrono.AbstractChronology.resolveYearOfEra "(Ljava/util/Map;Ljava/time/format/ResolverStyle;)
     ArtMethod[24][0x768d242b20]:  dex_method_idx[12695], m_idx[24], java.time.chrono.AbstractChronology.writeExternal "(Ljava/io/DataOutput;)
     ArtMethod[25][0x768d242490]:  dex_method_idx[13073], m_idx[25], java.time.chrono.HijrahChronology.writeReplace "()
     ArtMethod[26][0x768d241d40]:  dex_method_idx[13021], m_idx[26], java.time.chrono.HijrahChronology.date "(Ljava/time/chrono/Era;III)
     ArtMethod[27][0x768d241e90]:  dex_method_idx[13028], m_idx[27], java.time.chrono.HijrahChronology.dateNow "()
     ArtMethod[28][0x768d241ec0]:  dex_method_idx[13029], m_idx[28], java.time.chrono.HijrahChronology.dateNow "(Ljava/time/Clock;)
     ArtMethod[29][0x768d241ef0]:  dex_method_idx[13030], m_idx[29], java.time.chrono.HijrahChronology.dateNow "(Ljava/time/ZoneId;)
     ArtMethod[30][0x768d241fe0]:  dex_method_idx[13035], m_idx[30], java.time.chrono.HijrahChronology.dateYearDay "(Ljava/time/chrono/Era;II)
     ArtMethod[31][0x768d242e80]:  dex_method_idx[12990], m_idx[31], java.time.chrono.Chronology.getDisplayName "(Ljava/time/format/TextStyle;Ljava/util/Locale;)
     ArtMethod[32][0x768d2423a0]:  dex_method_idx[13062], m_idx[32], java.time.chrono.HijrahChronology.localDateTime "(Ljava/time/temporal/TemporalAccessor;)
     ArtMethod[33][0x768d242ee0]:  dex_method_idx[12997], m_idx[33], java.time.chrono.Chronology.period "(III)
     ArtMethod[34][0x768d2424c0]:  dex_method_idx[13076], m_idx[34], java.time.chrono.HijrahChronology.zonedDateTime "(Ljava/time/Instant;Ljava/time/ZoneId;)
     ArtMethod[35][0x768d2424f0]:  dex_method_idx[13077], m_idx[35], java.time.chrono.HijrahChronology.zonedDateTime "(Ljava/time/temporal/TemporalAccessor;)
     ArtMethod[36][0x768d241d10]:  dex_method_idx[13020], m_idx[36], java.time.chrono.HijrahChronology.date "(III)
     ArtMethod[37][0x768d241d70]:  dex_method_idx[13022], m_idx[37], java.time.chrono.HijrahChronology.date "(Ljava/time/temporal/TemporalAccessor;)
     ArtMethod[38][0x768d241e30]:  dex_method_idx[13026], m_idx[38], java.time.chrono.HijrahChronology.dateEpochDay "(J)
     ArtMethod[39][0x768d241fb0]:  dex_method_idx[13034], m_idx[39], java.time.chrono.HijrahChronology.dateYearDay "(II)
     ArtMethod[40][0x768d242070]:  dex_method_idx[13044], m_idx[40], java.time.chrono.HijrahChronology.eraOf "(I)
     ArtMethod[41][0x768d2420d0]:  dex_method_idx[13046], m_idx[41], java.time.chrono.HijrahChronology.eras "()
     ArtMethod[42][0x768d242100]:  dex_method_idx[13047], m_idx[42], java.time.chrono.HijrahChronology.getCalendarType "()
     ArtMethod[43][0x768d2421c0]:  dex_method_idx[13051], m_idx[43], java.time.chrono.HijrahChronology.getId "()
     ArtMethod[44][0x768d242370]:  dex_method_idx[13060], m_idx[44], java.time.chrono.HijrahChronology.isLeapYear "(J)
     ArtMethod[45][0x768d2423d0]:  dex_method_idx[13066], m_idx[45], java.time.chrono.HijrahChronology.prolepticYear "(Ljava/time/chrono/Era;I)
     ArtMethod[46][0x768d242400]:  dex_method_idx[13067], m_idx[46], java.time.chrono.HijrahChronology.range "(Ljava/time/temporal/ChronoField;)
     ArtMethod[47][0x768d241c80]:  dex_method_idx[13016], m_idx[47], java.time.chrono.HijrahChronology.checkValidDayOfYear "(I)
     ArtMethod[48][0x768d241cb0]:  dex_method_idx[13017], m_idx[48], java.time.chrono.HijrahChronology.checkValidMonth "(I)
     ArtMethod[49][0x768d241ce0]:  dex_method_idx[13018], m_idx[49], java.time.chrono.HijrahChronology.checkValidYear "(J)
     ArtMethod[50][0x768d241da0]:  dex_method_idx[13023], m_idx[50], java.time.chrono.HijrahChronology.date "(III)
     ArtMethod[51][0x768d241dd0]:  dex_method_idx[13024], m_idx[51], java.time.chrono.HijrahChronology.date "(Ljava/time/chrono/Era;III)
     ArtMethod[52][0x768d241e00]:  dex_method_idx[13025], m_idx[52], java.time.chrono.HijrahChronology.date "(Ljava/time/temporal/TemporalAccessor;)
     ArtMethod[53][0x768d241e60]:  dex_method_idx[13027], m_idx[53], java.time.chrono.HijrahChronology.dateEpochDay "(J)
     ArtMethod[54][0x768d241f20]:  dex_method_idx[13031], m_idx[54], java.time.chrono.HijrahChronology.dateNow "()
     ArtMethod[55][0x768d241f50]:  dex_method_idx[13032], m_idx[55], java.time.chrono.HijrahChronology.dateNow "(Ljava/time/Clock;)
     ArtMethod[56][0x768d241f80]:  dex_method_idx[13033], m_idx[56], java.time.chrono.HijrahChronology.dateNow "(Ljava/time/ZoneId;)
     ArtMethod[57][0x768d242010]:  dex_method_idx[13036], m_idx[57], java.time.chrono.HijrahChronology.dateYearDay "(II)
     ArtMethod[58][0x768d242040]:  dex_method_idx[13037], m_idx[58], java.time.chrono.HijrahChronology.dateYearDay "(Ljava/time/chrono/Era;II)
     ArtMethod[59][0x768d2420a0]:  dex_method_idx[13045], m_idx[59], java.time.chrono.HijrahChronology.eraOf "(I)
     ArtMethod[60][0x768d242130]:  dex_method_idx[13048], m_idx[60], java.time.chrono.HijrahChronology.getDayOfYear "(II)
     ArtMethod[61][0x768d242160]:  dex_method_idx[13049], m_idx[61], java.time.chrono.HijrahChronology.getEpochDay "(III)
     ArtMethod[62][0x768d242190]:  dex_method_idx[13050], m_idx[62], java.time.chrono.HijrahChronology.getHijrahDateInfo "(I)
     ArtMethod[63][0x768d2421f0]:  dex_method_idx[13052], m_idx[63], java.time.chrono.HijrahChronology.getMaximumDayOfYear "()
     ArtMethod[64][0x768d242220]:  dex_method_idx[13053], m_idx[64], java.time.chrono.HijrahChronology.getMaximumMonthLength "()
     ArtMethod[65][0x768d242250]:  dex_method_idx[13054], m_idx[65], java.time.chrono.HijrahChronology.getMaximumYear "()
     ArtMethod[66][0x768d242280]:  dex_method_idx[13055], m_idx[66], java.time.chrono.HijrahChronology.getMinimumMonthLength "()
     ArtMethod[67][0x768d2422b0]:  dex_method_idx[13056], m_idx[67], java.time.chrono.HijrahChronology.getMinimumYear "()
     ArtMethod[68][0x768d2422e0]:  dex_method_idx[13057], m_idx[68], java.time.chrono.HijrahChronology.getMonthLength "(II)
     ArtMethod[69][0x768d242310]:  dex_method_idx[13058], m_idx[69], java.time.chrono.HijrahChronology.getSmallestMaximumDayOfYear "()
     ArtMethod[70][0x768d242340]:  dex_method_idx[13059], m_idx[70], java.time.chrono.HijrahChronology.getYearLength "(I)
     ArtMethod[71][0x768d242460]:  dex_method_idx[13072], m_idx[71], java.time.chrono.HijrahChronology.resolveDate "(Ljava/util/Map;Ljava/time/format/ResolverStyle;)

可以看到 HijrahChronology 的 vtable 中,依次是从 Object类中继承的函数,从 AbstractChronology 类中继承的函数,以及其自身的 virtual methods;

从表中知道,HijrahChronology.localDateTime 这个函数的 method index是 32,那么这个index 32应该是受其父类影响了,

接下来看起其父类的 virtual methods:


(gdb) class_print_super 0x12e43690
current[0x12e43690]: java.time.chrono.HijrahChronology   [Normal]
super[0x12e43178]: java.time.chrono.AbstractChronology   [Abstract]
(gdb) class_print_vtable 0x12e43178
 
CLASS NAME:   java.time.chrono.AbstractChronology   [Abstract]
     first0: 0x12f109c4
     first1: 0x12f109c8
     ArtMethod[00][0x0070095660]:  dex_method_idx[02765], m_idx[00], java.lang.Object ??o.clone "()
     ArtMethod[01][0x768d2428b0]:  dex_method_idx[12663], m_idx[01], java.time.chrono.AbstractChronology.equals "(Ljava/lang/Object;)
     ArtMethod[02][0x00700956c0]:  dex_method_idx[02767], m_idx[02], java.lang.Object ??o.finalize "()
     ArtMethod[03][0x00700956f0]:  dex_method_idx[02768], m_idx[03], java.lang.Object ??o.getClass "()
     ArtMethod[04][0x768d2428e0]:  dex_method_idx[12669], m_idx[04], java.time.chrono.AbstractChronology.hashCode "()
     ArtMethod[05][0x0070095750]:  dex_method_idx[02773], m_idx[05], java.lang.Object ??o.notify "()
     ArtMethod[06][0x0070095780]:  dex_method_idx[02774], m_idx[06], java.lang.Object ??o.notifyAll "()
     ArtMethod[07][0x768d242af0]:  dex_method_idx[12694], m_idx[07], java.time.chrono.AbstractChronology.toString "()
     ArtMethod[08][0x00700957e0]:  dex_method_idx[02776], m_idx[08], java.lang.Object ??o.wait "()
     ArtMethod[09][0x0070095810]:  dex_method_idx[02777], m_idx[09], java.lang.Object ??o.wait "(J)
     ArtMethod[10][0x0070095840]:  dex_method_idx[02778], m_idx[10], java.lang.Object ??o.wait "(JI)
     ArtMethod[11][0x768d242820]:  dex_method_idx[12656], m_idx[11], java.time.chrono.AbstractChronology.addFieldValue "(Ljava/util/Map;Ljava/time/temporal/ChronoField;J)
     ArtMethod[12][0x768d242850]:  dex_method_idx[12657], m_idx[12], java.time.chrono.AbstractChronology.compareTo "(Ljava/lang/Object;)
     ArtMethod[13][0x768d242880]:  dex_method_idx[12658], m_idx[13], java.time.chrono.AbstractChronology.compareTo "(Ljava/time/chrono/Chronology;)
     ArtMethod[14][0x768d242910]:  dex_method_idx[12684], m_idx[14], java.time.chrono.AbstractChronology.resolveAligned "(Ljava/time/chrono/ChronoLocalDate;JJJ)
     ArtMethod[15][0x768d242940]:  dex_method_idx[12685], m_idx[15], java.time.chrono.AbstractChronology.resolveDate "(Ljava/util/Map;Ljava/time/format/ResolverStyle;)
     ArtMethod[16][0x768d242970]:  dex_method_idx[12686], m_idx[16], java.time.chrono.AbstractChronology.resolveProlepticMonth "(Ljava/util/Map;Ljava/time/format/ResolverStyle;)
     ArtMethod[17][0x768d2429a0]:  dex_method_idx[12687], m_idx[17], java.time.chrono.AbstractChronology.resolveYAA "(Ljava/util/Map;Ljava/time/format/ResolverStyle;)
     ArtMethod[18][0x768d2429d0]:  dex_method_idx[12688], m_idx[18], java.time.chrono.AbstractChronology.resolveYAD "(Ljava/util/Map;Ljava/time/format/ResolverStyle;)
     ArtMethod[19][0x768d242a00]:  dex_method_idx[12689], m_idx[19], java.time.chrono.AbstractChronology.resolveYD "(Ljava/util/Map;Ljava/time/format/ResolverStyle;)
     ArtMethod[20][0x768d242a30]:  dex_method_idx[12690], m_idx[20], java.time.chrono.AbstractChronology.resolveYMAA "(Ljava/util/Map;Ljava/time/format/ResolverStyle;)
     ArtMethod[21][0x768d242a60]:  dex_method_idx[12691], m_idx[21], java.time.chrono.AbstractChronology.resolveYMAD "(Ljava/util/Map;Ljava/time/format/ResolverStyle;)
     ArtMethod[22][0x768d242a90]:  dex_method_idx[12692], m_idx[22], java.time.chrono.AbstractChronology.resolveYMD "(Ljava/util/Map;Ljava/time/format/ResolverStyle;)
     ArtMethod[23][0x768d242ac0]:  dex_method_idx[12693], m_idx[23], java.time.chrono.AbstractChronology.resolveYearOfEra "(Ljava/util/Map;Ljava/time/format/ResolverStyle;)
     ArtMethod[24][0x768d242b20]:  dex_method_idx[12695], m_idx[24], java.time.chrono.AbstractChronology.writeExternal "(Ljava/io/DataOutput;)
     ArtMethod[25][0x768d242b50]:  dex_method_idx[12696], m_idx[25], java.time.chrono.AbstractChronology.writeReplace "()
     ArtMethod[26][0x768d242d90]:  dex_method_idx[12976], m_idx[26], java.time.chrono.Chronology.date "(Ljava/time/chrono/Era;III)
     ArtMethod[27][0x768d242dc0]:  dex_method_idx[12979], m_idx[27], java.time.chrono.Chronology.dateNow "()
     ArtMethod[28][0x768d242df0]:  dex_method_idx[12980], m_idx[28], java.time.chrono.Chronology.dateNow "(Ljava/time/Clock;)
     ArtMethod[29][0x768d242e20]:  dex_method_idx[12981], m_idx[29], java.time.chrono.Chronology.dateNow "(Ljava/time/ZoneId;)
     ArtMethod[30][0x768d242e50]:  dex_method_idx[12983], m_idx[30], java.time.chrono.Chronology.dateYearDay "(Ljava/time/chrono/Era;II)
     ArtMethod[31][0x768d242e80]:  dex_method_idx[12990], m_idx[31], java.time.chrono.Chronology.getDisplayName "(Ljava/time/format/TextStyle;Ljava/util/Locale;)
     ArtMethod[32][0x768d242eb0]:  dex_method_idx[12994], m_idx[32], java.time.chrono.Chronology.localDateTime "(Ljava/time/temporal/TemporalAccessor;)
     ArtMethod[33][0x768d242ee0]:  dex_method_idx[12997], m_idx[33], java.time.chrono.Chronology.period "(III)
     ArtMethod[34][0x768d242f10]:  dex_method_idx[13002], m_idx[34], java.time.chrono.Chronology.zonedDateTime "(Ljava/time/Instant;Ljava/time/ZoneId;)
     ArtMethod[35][0x768d242f40]:  dex_method_idx[13003], m_idx[35], java.time.chrono.Chronology.zonedDateTime "(Ljava/time/temporal/TemporalAccessor;)
     ArtMethod[36][0x768d242b80]:  dex_method_idx[12975], m_idx[36], java.time.chrono.Chronology.date "(III)
     ArtMethod[37][0x768d242bb0]:  dex_method_idx[12977], m_idx[37], java.time.chrono.Chronology.date "(Ljava/time/temporal/TemporalAccessor;)
     ArtMethod[38][0x768d242be0]:  dex_method_idx[12978], m_idx[38], java.time.chrono.Chronology.dateEpochDay "(J)
     ArtMethod[39][0x768d242c10]:  dex_method_idx[12982], m_idx[39], java.time.chrono.Chronology.dateYearDay "(II)
     ArtMethod[40][0x768d242c40]:  dex_method_idx[12985], m_idx[40], java.time.chrono.Chronology.eraOf "(I)
     ArtMethod[41][0x768d242c70]:  dex_method_idx[12986], m_idx[41], java.time.chrono.Chronology.eras "()
     ArtMethod[42][0x768d242ca0]:  dex_method_idx[12989], m_idx[42], java.time.chrono.Chronology.getCalendarType "()
     ArtMethod[43][0x768d242cd0]:  dex_method_idx[12991], m_idx[43], java.time.chrono.Chronology.getId "()
     ArtMethod[44][0x768d242d00]:  dex_method_idx[12993], m_idx[44], java.time.chrono.Chronology.isLeapYear "(J)
     ArtMethod[45][0x768d242d30]:  dex_method_idx[12998], m_idx[45], java.time.chrono.Chronology.prolepticYear "(Ljava/time/chrono/Era;I)
     ArtMethod[46][0x768d242d60]:  dex_method_idx[12999], m_idx[46], java.time.chrono.Chronology.range "(Ljava/time/temporal/ChronoField;)

可以看到 index=32处的 method:

ArtMethod[32][0x768d242eb0]: dex_method_idx[12994], m_idx[32], java.time.chrono.Chronology.localDateTime "(Ljava/time/temporal/TemporalAccessor;)

可以得到的信息是:

这个virtual method的 method index是 32,说明前面HijrahChronology.localDateTime函数的 method index确实是受其影响的(实际是相当于 HijrahChronology类 Override了AbstractChronology 类中的 localDateTime函数)。

另外,这个函数的 dex_method_idx = 12994,那么问题来了,出现冲突了:

1.这个函数 dex_method_idx是 12994,对应的也是 Chronology.localDateTime,其实是从 Chronology interface的 default函数拷贝过来的,但是由于其自身的函数多,导致分配 method_idx时,给分配成 32了

2.前面看过Chronology interface中记录的 localDateTime这个函数,dex_method_idx是 12994,method_idx 是 19

实际上,它们代表的是一个函数,只不过对应的 method_idx 不同,那么 resolve method 的时候,怎么办 ?在 dex_cache_resolved_methods_中怎么保存?

实际的情况是,谁先 resolve了,那么保存的就是谁,比如,当前处是,先Resolve的是 method_idx是32的这个函数:

dex_cache_resolved_methods_ = 0x700f8710;

interface method 是:

(gdb) x /gx 0x700f8710+12994*8

0x70111d20: 0x000000768d242eb0

所以,至此我们清楚了,为何 dex_cache_resolved_methods_中,保存的对应Chronology.localDateTime的 ArtMethod 与Chronology类中,记录的ArtMethod不同,

根本原因在于 AbstractChronology类实现 Chronology interface时,继承了其 default Method,会拷贝一份到AbstractChronology类的virtual methods表中,

dex_method_index保持一致,但是会修改 method index;这样如果这个函数先被调用,就会把这个函数设置到 dex_cache_resolved_methods_中。

使用其查找 iftable时,就会使用了错误的 method index,从而查到错误的数据。

4.解决方案

了解了问题原因,解决方案有两种:

1.从根本上解决问题,在ResolveMethod的时候,对从 interface接口继承过来的函数做特殊处理

2.workaround,在写java 代码时,避开这种情况,没有必要,用到 Abstract Class的同时,还继续使用 Interface的Default函数

相关内容