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 <unknown>
#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<void*>(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 <unknown> #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.<init>() // 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.<init>(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<mirror::Object> this_object(raw_this_object);
Handle<mirror::Class> 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<uintptr_t>(code),
reinterpret_cast<uintptr_t>(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_ = {
<art::mirror::ObjectReference<false, art::mirror::Object>> = {
reference_ = 0
}, <No data fields>}
},
access_flags_ = {
<std::__1::__atomic_base<unsigned int, true>> = {
<std::__1::__atomic_base<unsigned int, false>> = {
__a_ = 0
}, <No data fields>}, <No data fields>},
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函数;
/*
* 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_ = {
<art::mirror::ObjectReference<false, art::mirror::Object>> = {
reference_ = 316946064
}, <No data fields>}
},
access_flags_ = {
<std::__1::__atomic_base<unsigned int, true>> = {
<std::__1::__atomic_base<unsigned int, false>> = {
__a_ = 528449
}, <No data fields>}, <No data fields>},
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_ = {
<art::mirror::ObjectReference<false, art::mirror::Object>> = {
reference_ = 316945944
}, <No data fields>}
},
access_flags_ = {
<std::__1::__atomic_base<unsigned int, true>> = {
<std::__1::__atomic_base<unsigned int, false>> = {
__a_ = 5767169
}, <No data fields>}, <No data fields>},
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函数