Android安全交流群:478084054
前面笔记贴过老罗博客的一段话(非原文,简化了一下):
“ART运行时会为类方法生成相应的本地机器指令,这些本地机器指令可能会调用外部函数,这就涉及到模块依赖问题,就好像我们在编写程序时,需要依赖C库提供的接口一样。ART运行时支持两种类型的Backend:Portable和Quick。Portable类型的Backend通过静态链接器生成本地机器指令,通过重定位技术来处理模块依赖问题。这对熟悉linker动态加载过程的程序员来说很容易理解。而Quick类型的Backend生成的本地机器指令用另外一种方式来处理模块之间的依赖关系。简单的说,就是ART运行时会在每一个线程的TLS(线程本地区域)提供一个函数表,本地机器指令通过它来调用其它模块的函数。这使得生成的OAT文件在加载时不需要再处理模块之间的依赖关系,也就省去了重定位,不需要通过系统的动态链接器提供的dlopen来加载。这样OAT文件在加载时就会更快,这也是称其为Quick的缘由。”
本篇笔记就看看这段话中提到的TLS函数表(还是以6.0源码为例)。
在ART初始化过程中的Runtime::Init函数中,有如下代码:
作用是将当前线程注册到ART运行时的线程列表中。
继续看Thread::Init函数。
最后调用thread_list->Register(this),就是将当前线程注册到ART运行时的线程列表中去。
在这之前,还有一步:InitTlsEntryPoints(),在InitTlsEntryPoints中调用InitEntryPoints。
调用InitEntryPoints时传入的:interpreter_entrypoints、jni_entrypoints、quick_entrypoints三个参数,就是老罗那段话中提到的TLS保存的用于解决模块依赖关系的函数表。
这些函数表在InitEntryPoints函数中被初始化。
继续看InitEntryPoints(以arm版本为例)。
首先初始化interpreter_entrypoints,保存的是解释器要用到的跳转函数。
只包含两项,分别是artInterpreterToInterpreterBridge和artInterpreterToCompiledCodeBridge。前者用于从一个解释执行的类方法跳到另一个也是解释执行的类方法去执行,后者用于从一个解释执行的类方法跳到另一个以本地机器指令执行的类方法去执行。
然后初始化jni_entrypoints,保存的是JNI调用相关的跳转函数。
如果在生成的本地机器指令中,需要调用一个JNI函数,那么就需要先通过art_jni_dlsym_lookup_stub找到正确的JNI函数,然后再执行。
剩下的就都是quick_entrypoints的初始化了,覆盖了所有的Quick后端生成的本地机器指令要用到的跳转函数。
其中包含了art_quick_to_interpreter_bridge,用于从一个以本地机器指令执行的类方法跳到另一个解释执行的类方法去执行。
这些跳转表中有很多bridge和trampoline函数,我很想知道它们到底是怎么工作的。后面再去看吧。
感谢老罗博客。
网友评论