问题描述
在Android5,0及其以上的版本,系统会通过dex2oat将应用编译为ARM机器指令来提高执行速度。而对于ARM指令,我想请问一下对一个类的方法是如何获取他的实际跳转地址的呢?
例如调用telephonymanager.getdeviceid(),其对应的dex code为
iget-object telephonymanager;
invoke telephonymanager.getdeviceid其实际的跳转地址在我的写的应用中的 获取过程为 :
[r1,#16]->r5,
r5->r1
[r1,#0]->r0
[r0,#572]->r0
[r0,#40]->lr
blx lr 所以我想请问一下这一段获取其实际跳转的地址的具体意义,其中这些数值 如16 572等是如何计算出来的, 麻烦大家帮忙解释一下,如果给一份学习资料也是挺好的,因为我现在找到的所有关于arm调用的资料都是 函数的调用,而对这种类的方法的调用所讲的比较少。
好奇一下,题主是用哪种办法查看ART编译出来的机器码的?oatdump么?
题主帖的那段代码其实挺明确的,就是个普通的基于vtable的虚方法调用而已:
[r1,#16]->r5, ;; r5:TelephonyManager = this.telephonymanager
r5->r1 ;; r1:TelephonyManager = r5
[r1,#0]->r0 ;; r0:Class = telephonymanager.klass_
[r0,#572]->r0 ;; r0:ArtMethod = r0+vtable_offset+vtable_offset_of(getdeviceid)
[r0,#40]->lr ;; lr = r0.entry_point_from_quick_compiled_code_
blx lr ;; call telephonymanager.getdeviceid
其实跟C++的基于vtable的虚函数调用非常相似。
只不过ART里对象的虚函数表(vtable)被嵌在Class对象的末尾,而对象实例开头的字段指向的是这个Class对象而不是像C++的常见实现那样指向vtable的第0项。
另外ART的vtable里每一项装的不直接是函数的“入口地址”(因为可能有多种入口),而是一个指向ArtMethod对象的指针。真正的“入口地址”存在ArtMethod的字段里。
这些对象之间的引用关系是这样的:
Object Class
+0 [ klass_ ] --> [ ... ]
[ monitor_ ] [ ... ]
[ instance fields ] [ vtable[0] ]
[ vtable[1] ]
[ ... ] ArtMethod
[ vtable[n] ] --> [ ... ]
[ ... ] [ ... ]
[ entry_point_from_quick_compiled_code_ ] --> [ compiled code ]
[ ... ]
可以看到vtable被嵌入在Class对象之中,而vtable里的项是指向ArtMethod的指针。
例子中的数字,
- 16:这是题主给出的例子中this对象里telephonymanager字段所在的偏移量。dex2oat将一个Java类转换为ART能理解的形式时会为其计算Class元数据的内容布局,确定元数据的数量、种类和位置,例如说vtable有多少项,分别都是哪些方法;以及对象实例的内存布局,确定所有Java字段在对象中的偏移量。
- 0:这是ART中Java对象的klass_字段的偏移量,指向Class对象
- 572:这是在Class对象中vtable的偏移量加上getdeviceid()方法对应的项在vtable中的偏移量。在Class对象中的这个偏移量读到的就是getdeviceid()对应的vtable项——一个ArtMethod指针
- 40:从ArtMethod中找到“入口地址”。这是个普通的C++字段,这个偏移量是由C++编译器算出来的。
在ART的一些相关的runtime代码:
- Object:用来表现Java对象的C++结构
- Class:用来表现Java类的元数据的C++结构,同时也表现java.lang.Class实例
- ArtMethod:用来表现Java方法的元数据的C++结构
- ClassLinker::LinkFields():计算对象布局
- ClassLinker::LinkVirtualMethods():计算类的vtable和IMT(interface method table)布局