技术领域
[0001] 本
发明涉及计算机技术领域,具体涉及一种内联控制方法及装置。
背景技术
[0002] 在Java编程语言和环境中,即时编译(just-in-time,JIT)是一个把Java的字节码(包括需要被解释的指令的程序)转换成可以直接发送给处理器的指令的程序。当写好一个Java程序后,
源语言的语句将由Java编译器编译成字节码,而不是编译成与某个特定的处理器
硬件平台对应的指令代码。字节码是可以发送给任何平台并且能在那个平台上运行的独立于平台的代码。
发明内容
[0003] 鉴于上述问题,提出了本发明以便提供一种克服上述问题或者至少部分地解决上述问题的内联控制方法及装置。
[0004] 依据本发明的一个方面,提供一种内联控制方法,该方法用于在Java编译中对当前Java方法中JIT内联操作进行控制;所述方法包括:
[0005] 确定JIT编译中执行内联操作的函数地址;
[0006] 设置用于替换上述执行内联操作的函数的返回函数;
[0007] 控制在所述执行内联操作的函数地址执行所述返回函数,从而禁止JIT编译中的内联操作。
[0008] 在一种可选方式中,所述确定JIT编译中执行内联操作的函数地址包括:
[0009] 确定执行内联操作的函数名称;
[0010] 根据所述执行内联操作的函数名称,通过调用寻找函数地址的命令,确定JIT编译中执行内联操作的函数地址。
[0011] 在一种可选方式中,还包括:
[0012] 定义用于存放所述执行内联操作的函数地址的地址存放函数;
[0013] 在得到所述执行内联操作的函数地址之后,利用所述地址存放函数存放所述执行内联操作的函数地址。
[0014] 在一种可选方式中,所述替换上述执行内联操作的函数的返回函数包括:
[0015] 自定义不执行操作的所述返回函数,用于替换所述执行内联操作的函数。
[0016] 在一种可选方式中,控制在所述执行内联操作的函数地址执行所述返回函数包括:
[0017] 对所述执行内联操作的函数执行Hook操作,从而使所述执行内联操作的函数跳转到所述返回函数。
[0018] 依据本发明的一个方面,提供一种内联控制装置,用于在Java编译中对当前
进程中JIT内联操作进行控制;所述装置包括:
[0019] 地址确定单元,用于确定JIT编译中执行内联操作的函数地址;
[0020] 返回函数设置单元,用于设置替换JIT编译中执行内联操作的返回函数;
[0021] 执行控制单元,用于控制在所述执行内联操作的函数地址执行所述返回函数,从而禁止JIT编译中的内联操作。
[0022] 在一种可选方式中,所述地址确定单元具体用于:
[0023] 确定执行内联操作的函数名称;以及,根据所述执行内联操作的函数名称,通过调用寻找函数地址的命令,确定JIT编译中执行内联操作的函数地址。
[0024] 在一种可选方式中,所述地址确定单元还用于:
[0025] 定义用于存放所述执行内联操作的函数地址的地址存放函数;在得到所述执行内联操作的函数地址之后,利用所述地址存放函数存放所述执行内联操作的函数地址。
[0026] 在一种可选方式中,所述返回函数设置单元具体用于:
[0027] 自定义不执行操作的所述返回函数,用于替换所述执行内联操作的函数。
[0028] 在一种可选方式中,所述执行控制单元具体用于:
[0029] 对所述执行内联操作的函数执行Hook操作,从而使所述执行内联操作的函数跳转到所述返回函数。
[0030] 可见,本发明
实施例提供的内联控制方法,通过将内联函数替换为返回函数,使得执行JIT编译中内联操作的代码失效,通过禁止内联操作,从而使得对一个方法的调用都必定会跳转到此方法的入口代码处,进而能够保证Hook方案的有效性。
[0031] 本发明实施例比直接关闭整个JIT编译流程具有明显的优势:因为虽然关闭整个JIT编译流程也能够关闭内联,从而解决Hook失效的问题,但是这样会失去JIT编译
加速代码执行速度的优点,对应用的运行速度有很大影响。而本发明实施例上述方案能够在基本不影响应用执行速度的同时,解决Hook失效的问题。
[0032] 上述说明仅是本发明技术方案的概述,为了能够更清楚了解本发明的技术手段,而可依照
说明书的内容予以实施,并且为了让本发明的上述和其它目的、特征和优点能够更明显易懂,以下特举本发明的具体实施方式。
附图说明
[0033] 通过阅读下文优选实施方式的详细描述,各种其他的优点和益处对于本领域普通技术人员将变得清楚明了。附图仅用于示出优选实施方式的目的,而并不认为是对本发明的限制。而且在整个附图中,用相同的参考符号表示相同的部件。在附图中:
[0034] 图1示出了根据本发明一个实施例的ART环境JIT编译示意图;
[0035] 图2示出了根据本发明一个实施例的内联控制方法
流程图;
[0036] 图3示出了根据本发明一个实施例的内联控制装置结构示意图。
具体实施方式
[0037] 下面将参照附图更详细地描述本公开的示例性实施例。虽然附图中显示了本公开的示例性实施例,然而应当理解,可以以各种形式实现本公开而不应被这里阐述的实施例所限制。相反,提供这些实施例是为了能够更透彻地理解本公开,并且能够将本公开的范围完整的传达给本领域的技术人员。
[0038] 参见图1,示出了根据本发明一个实施例的ART环境JIT编译示意图。
[0039] ART(Anroid RunTime)是一个Android
虚拟机。AOT(Ahead of Time)是指在运行以前就把中间代码静态编译成本地代码,而JIT(Just inTime)则是在运行时动态编译。
[0040] 在使用Java开发Android(安卓)时,在编译打包APK文件时,会经过以下流程:Java编译器将应用中所有Java文件编译为class文件;dx工具将应用编译输出的类文件转换为Dalvik字节码,即dex文件。之后经过签名、对齐等操作变为APK文件。如果每次执行代码,都需要将dex代码翻译为
微处理器指令,然后交给系统处理,这样效率不高。为了解决这个问题,增加了JIT编译器,当App运行时,每当遇到一个新类,JIT编译器就会对这个类进行编译,经过编译后的代码,会被优
化成相当精简的原生型指令码(即native code),这样在下次执行到相同逻辑的时候,速度就会更快。
[0041] 当然使用JIT也不一定加快执行速度,如果大部分代码的执行次数很少,那么编译花费的时间不一定少于执行dex的时间。所以JIT不对所有dex代码进行编译,而是只编译执行次数较多的dex为本地机器码。例如图1中仅对热代码(hot code)使用JIT;对于冷代码(cold code)则使用解释器(interpreter)进行处理。其中热代码和冷代码是根据代码的出现
频率相对而言的。
[0042] 从图1中可以看出:对于oat文件直接采取ART进行执行;对于dex文件,首先需要判断该dex代码是热代码还是冷代码,对于热代码采取JIT编译;对于冷代码采取解释器处理。
[0043] 可见,一个Java方法有三种状态:1、没有被编译;2、被OAT编译;3.被JIT编译。虚拟机执行方法时,如果方法处于第一种状态,那么就用解释器解释执行,其他两种状态都是去执行编译成的本地(native)代码。如果一个方法被频繁以第一种方式解释执行,那么就会对这个方法进行JIT编译,从而提高执行效率。JIT编译的线程为后台线程。
[0044] 在编译过程中,一个Method就是一个图(Graph),每个
节点是指令或者调用方法等。内联(Inline)就是分析当前的Mehtod构成的Graph,找到其调用的其他Method的Graph,将后面的Graph合并到前面的Graph中去。内联函数具有一般函数的特性,它与一般函数所不同之处只在于函数调用的处理。一般函数进行调用时,要将程序执行权转到被调用函数中,然后再返回到调用它的函数中;而内联函数在调用时,是将调用表达式用内联函数体来替换。
[0045] 钩子(Hook),从广义上来讲是控制函数的执行。在执行被Hook的函数时不再执行原始的函数体,而是先转到自定义的函数体内,自定义的函数体可以根据情况判断是否调用原始函数,Hook一个函数主要应用于
修改原始函数的参数或者修改其返回值,一般都会调用原始函数。Hook可以是针对C函数的Hook,也可以是针对Java函数(方法)的Hook。。
[0046] 在研究过程中发现,JIT内联会致使Hook(钩子)失效。原因分析如下。Hook需要一个明确的代码
位置,例如,可以根据一个Java方法明确地得到其代码入口点位置。调用一个方法时,如果这个方法没有被内联,会跳转到其入口点位置执行代码,此时对该方法入口点Hook即能Hook此方法。但是如果这个方法被内联,调用处就会执行与此方法功能等效的一段代码而不会跳转到该方法的代码入口位置执行。因此上述hook就会失效,并且与此方法功能等效的代码位置无法
定位,因此会导致hook失效。本文中所述JIT内联会使Hook失效是指会使对Java函数的Hook失效,而非C函数。
[0047] 本发明实施例提供一种解决内联控制的方法,用于解决JIT内联致使Hook失效的问题。
[0048] 参见图2,该方法用于在Java编译中对当前Java方法中内联操作进行控制;包括步骤S201-S203。
[0049] S201:确定JIT编译中执行内联操作的函数地址。
[0050] 在一种可选方式中,确定JIT编译中执行内联操作的函数地址的过程可以包括:
[0051] (1)确定执行内联操作的函数名称;
[0052] (2)根据执行内联操作的函数名称,通过调用寻找函数地址的命令,确定JIT编译中执行内联操作的函数地址。
[0053] 在一种可选方式中,可以通过如下方式存放地址:定义用于存放执行内联操作的函数地址的地址存放函数;在得到执行内联操作的函数地址之后,利用地址存放函数存放执行内联操作的函数地址。
[0054] S202:设置用于替换上述执行内联操作的函数的返回函数。
[0055] 该返回函数不执行具体的操作,其作用是返回到Java方法入口。具体实现中,可自定义不执行操作的返回函数,用于替换执行内联操作的函数。
[0056] S203:控制在执行内联操作的函数地址执行返回函数,从而禁止JIT编译中的内联操作。
[0057] 在一种可选方式中,通过以下方式控制在执行内联操作的函数地址执行返回函数:对执行内联操作的函数执行Hook(钩子)操作,从而使执行内联操作的函数跳转到返回函数。
[0058] 本发明实施例中,首先确定出JIT编译中执行内联操作的函数地址,然后Hook该执行内联操作的函数地址到一个返回函数,使执行内联操作的函数失效。
[0059] 例如,使执行内联操作的代码失效的具体方式可以是:
[0060] hook/art/compiler/optimizing/inliner.cc下的HInliner::Run()方法,使它不执行任何内容,具体到程序中是找到/system/lib/libart-compiler.so导出的“_ZN3art8HInliner3RunEv”所在的内存地址,然后对该函数进行Hook即可。
[0061] 根据前面分析的,“JIT内联会致使Hook(钩子)失效”的原因在于:由于内联,调用处就会执行与此方法功能等效的一段代码而不会跳转到该方法的代码入口位置执行,因此上述hook就会失效。而本发明实施例中,首先确定JIT编译中执行内联操作的函数地址;然后控制在该函数地址执行一个返回函数,使得调用处跳转到该方法的代码入口位置执行。可见,通过执行返回函数使JIT编译中内联(inline)操作的代码失效,从而禁止了内联操作,使得对一个方法的调用都必定会跳转到此方法的入口代码处,进而能够保证hook方案的有效性。
[0062] 下面,以一个具体实例对本发明实施例的实现过程进行示例性说明。
[0063] 步骤1:寻找到进行JIT编译中内联操作的函数地址。
[0064] 这个函数从源码
角度而言例如是/art/compiler/optimizing/inliner.cc下的HInliner::Run()函数;从运行期间而言例如是/system/lib/libart-compiler.so的”_ZN3art8HInliner3RunEv”。
[0065] 例如,步骤1示例性代码为:
[0066] void*funAddr;//用于存放函数地址
[0067] getFunctionAddr(“/system/lib/libart-compiler.so”,"_ZN3art8HInliner3RunEv",&funAddr);//寻找函数地址
[0068] 步骤2:定义一个返回函数。
[0069] 这个返回函数不执行操作,直接返回。用于替换上述找到的执行内联操作的函数。
[0070] 例如,步骤2示例性代码为:
[0071] void myInlinerRun(void*i1,void*i2,void*i3,void*i4,void*i5,void*i6,void*i7,void*i8,void*i9,void*i10){
[0072] return;
[0073] }
[0074] 步骤3:Hook步骤1寻找到的内联操作的函数,使其跳转到步骤2中自定义的返回函数。注意这里的Hook是指hook一个c函数。
[0075] 例如,步骤3示例性代码为:
[0076] void*orgFun;
[0077] hook((uint32_t)funAddr,(uint32_t)myInlinerRun,(uint32_t**)&orgFun)[0078] 可见,本发明实施例使得执行JIT编译中内联(inline)操作的代码失效,通过禁止内联操作,从而使得对一个方法的调用都必定会跳转到此方法的入口代码处,进而能够保证Hook方案的有效性。从效果上而言,本发明实施例比直接关闭整个JIT编译流程具有明显的优势:因为虽然关闭整个JIT编译流程也能够关闭内联,从而解决Hook失效的问题,但是这样会失去JIT编译加速代码执行速度的优点,对应用的运行速度有很大影响。而本发明实施例上述方案能够在基本不影响应用执行速度的同时,解决Hook失效的问题。
[0079] 与上述JIT内联控制方法相对应,本发明实施例还提供一种JIT内联控制装置,用于在Java编译中对当前Java方法中JIT内联操作进行控制。
[0080] 参见图3,该JIT内联控制装置包括:
[0081] 地址确定单元301,用于确定JIT编译中执行内联操作的函数地址;
[0082] 返回函数设置单元302,用于设置用于替换上述执行内联操作的函数的返回函数;
[0083] 执行控制单元303,用于控制在所述执行内联操作的函数地址执行所述返回函数,从而禁止JIT编译中的内联操作。
[0084] 在一种可选方式中,
[0085] 所述地址确定单元301具体用于:确定执行内联操作的函数名称;以及,根据所述执行内联操作的函数名称,通过调用寻找函数地址的命令,确定JIT编译中执行内联操作的函数地址。
[0086] 在一种可选方式中,
[0087] 所述地址确定单元301还用于:
[0088] 定义用于存放所述执行内联操作的函数地址的地址存放函数;在得到所述执行内联操作的函数地址之后,利用所述地址存放函数存放所述执行内联操作的函数地址。
[0089] 在一种可选方式中,
[0090] 所述返回函数设置单元302具体用于:自定义不执行操作的所述返回函数,用于替换所述执行内联操作的函数。
[0091] 在一种可选方式中,
[0092] 所述执行控制单元303具体用于:对所述执行内联操作的函数执行Hook操作,从而使所述执行内联操作的函数跳转到所述返回函数。
[0093] 在此提供的
算法和显示不与任何特定计算机、虚拟系统或者其它设备固有相关。各种通用系统也可以与基于在此的示教一起使用。根据上面的描述,构造这类系统所要求的结构是显而易见的。此外,本发明也不针对任何特定编程语言。应当明白,可以利用各种编程语言实现在此描述的本发明的内容,并且上面对特定语言所做的描述是为了披露本发明的最佳实施方式。
[0094] 在此处所提供的说明书中,说明了大量具体细节。然而,能够理解,本发明的实施例可以在没有这些具体细节的情况下实践。在一些实例中,并未详细示出公知的方法、结构和技术,以便不模糊对本说明书的理解。
[0095] 类似地,应当理解,为了精简本公开并帮助理解各个发明方面中的一个或多个,在上面对本发明的示例性实施例的描述中,本发明的各个特征有时被一起分组到单个实施例、图、或者对其的描述中。然而,并不应将该公开的方法解释成反映如下意图:即所要求保护的本发明要求比在每个
权利要求中所明确记载的特征更多的特征。更确切地说,如下面的权利要求书所反映的那样,发明方面在于少于前面公开的单个实施例的所有特征。因此,遵循具体实施方式的权利要求书由此明确地并入该具体实施方式,其中每个权利要求本身都作为本发明的单独实施例。
[0096] 本领域那些技术人员可以理解,可以对实施例中的设备中的模
块进行自适应性地改变并且把它们设置在与该实施例不同的一个或多个设备中。可以把实施例中的模块或单元或组件组合成一个模块或单元或组件,以及此外可以把它们分成多个子模块或子单元或子组件。除了这样的特征和/或过程或者单元中的至少一些是相互排斥之外,可以采用任何组合对本说明书(包括伴随的权利要求、
摘要和附图)中公开的所有特征以及如此公开的任何方法或者设备的所有过程或单元进行组合。除非另外明确陈述,本说明书(包括伴随的权利要求、摘要和附图)中公开的每个特征可以由提供相同、等同或相似目的的替代特征来代替。
[0097] 此外,本领域的技术人员能够理解,尽管在此所述的一些实施例包括其它实施例中所包括的某些特征而不是其它特征,但是不同实施例的特征的组合意味着处于本发明的范围之内并且形成不同的实施例。例如,在下面的权利要求书中,所要求保护的实施例的任意之一都可以以任意的组合方式来使用。
[0098] 本发明的各个部件实施例可以以
硬件实现,或者以在一个或者多个处理器上运行的
软件模块实现,或者以它们的组合实现。本领域的技术人员应当理解,可以在实践中使用微处理器或者数字
信号处理器(DSP)来实现根据本发明实施例的
插件调用通知栏的装置中的一些或者全部部件的一些或者全部功能。本发明还可以实现为用于执行这里所描述的方法的一部分或者全部的设备或者装置程序(例如,
计算机程序和计算机程序产品)。这样的实现本发明的程序可以存储在计算机可读介质上,或者可以具有一个或者多个信号的形式。这样的信号可以从因特网
网站上下载得到,或者在载体信号上提供,或者以任何其他形式提供。
[0099] 应该注意的是上述实施例对本发明进行说明而不是对本发明进行限制,并且本领域技术人员在不脱离所附权利要求的范围的情况下可设计出替换实施例。在权利要求中,不应将位于括号之间的任何参考符号构造成对权利要求的限制。单词“包含”不排除存在未列在权利要求中的元件或步骤。位于元件之前的单词“一”或“一个”不排除存在多个这样的元件。本发明可以借助于包括有若干不同元件的硬件以及借助于适当编程的计算机来实现。在列举了若干装置的单元权利要求中,这些装置中的若干个可以是通过同一个硬件项来具体体现。单词第一、第二、以及第三等的使用不表示任何顺序。可将这些单词解释为名称。
[0100] 可见,本发明实施例提供如下技术方案:
[0101] A1、一种内联控制方法,该方法用于在Java编译中对当前进程中JIT内联操作进行控制;所述方法包括:
[0102] 确定JIT编译中执行内联操作的函数地址;
[0103] 设置用于替换上述JIT编译中执行内联操作的函数;
[0104] 控制在所述执行内联操作的函数地址执行所述返回函数,从而禁止JIT编译中的内联操作。
[0105] A2、根据A1所述的方法,所述确定JIT编译中执行内联操作的函数地址包括:
[0106] 确定执行内联操作的函数名称;
[0107] 根据所述执行内联操作的函数名称,通过调用寻找函数地址的命令,确定JIT编译中执行内联操作的函数地址。
[0108] A3、根据A2所述的方法,还包括:
[0109] 定义用于存放所述执行内联操作的函数地址的地址存放函数;
[0110] 在得到所述执行内联操作的函数地址之后,利用所述地址存放函数存放所述执行内联操作的函数地址。
[0111] A4、根据A1-A3任一项所述的方法,所述设置用于替换上述JIT编译中执行内联操作的函数包括:
[0112] 自定义不执行操作的所述返回函数,用于替换所述执行内联操作的函数。
[0113] A5、根据A1-A4任一项所述的方法,控制在所述执行内联操作的函数地址执行所述返回函数包括:
[0114] 对所述执行内联操作的函数执行Hook操作,从而使所述执行内联操作的函数跳转到所述返回函数。
[0115] B6、一种内联控制装置,用于在Java编译中对当前Java方法中JIT内联操作进行控制;所述装置包括:
[0116] 地址确定单元,用于确定JIT编译中执行内联操作的函数地址;
[0117] 返回函数设置单元,用于设置用于替换上述JIT编译中执行内联操作的函数的返回函数;
[0118] 执行控制单元,用于控制在所述执行内联操作的函数地址执行所述返回函数,从而禁止JIT编译中的内联操作。
[0119] B7、根据B6所述的装置,所述地址确定单元具体用于:确定执行内联操作的函数名称;以及,根据所述执行内联操作的函数名称,通过调用寻找函数地址的命令,确定JIT编译中执行内联操作的函数地址。
[0120] B8、根据B7所述的装置,所述地址确定单元还用于:
[0121] 定义用于存放所述执行内联操作的函数地址的地址存放函数;在得到所述执行内联操作的函数地址之后,利用所述地址存放函数存放所述执行内联操作的函数地址。
[0122] B9、根据B6-B8任一项所述的装置,所述返回函数设置单元具体用于:自定义不执行操作的所述返回函数,用于替换所述执行内联操作的函数。
[0123] B10、根据B6-B9任一项所述的装置,所述执行控制单元具体用于:对所述执行内联操作的函数执行Hook操作,从而使所述执行内联操作的函数跳转到所述返回函数。