• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2008 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 /*
18  * Perform some simple bytecode optimizations, chiefly "quickening" of
19  * opcodes.
20  */
21 #include "Dalvik.h"
22 #include "libdex/InstrUtils.h"
23 
24 #include <zlib.h>
25 
26 #include <stdlib.h>
27 
28 /*
29  * Virtual/direct calls to "method" are replaced with an execute-inline
30  * instruction with index "idx".
31  */
32 struct InlineSub {
33     Method* method;
34     int     inlineIdx;
35 };
36 
37 
38 /* fwd */
39 static void optimizeMethod(Method* method, bool essentialOnly);
40 static bool rewriteInstField(Method* method, u2* insns, OpCode quickOpc,
41     OpCode volatileOpc);
42 static bool rewriteStaticField(Method* method, u2* insns, OpCode volatileOpc);
43 static bool rewriteVirtualInvoke(Method* method, u2* insns, OpCode newOpc);
44 static bool rewriteEmptyDirectInvoke(Method* method, u2* insns);
45 static bool rewriteExecuteInline(Method* method, u2* insns,
46     MethodType methodType);
47 static bool rewriteExecuteInlineRange(Method* method, u2* insns,
48     MethodType methodType);
49 
50 
51 /*
52  * Create a table of inline substitutions.
53  *
54  * TODO: this is currently just a linear array.  We will want to put this
55  * into a hash table as the list size increases.
56  */
dvmCreateInlineSubsTable(void)57 InlineSub* dvmCreateInlineSubsTable(void)
58 {
59     const InlineOperation* ops = dvmGetInlineOpsTable();
60     const int count = dvmGetInlineOpsTableLength();
61     InlineSub* table;
62     Method* method;
63     ClassObject* clazz;
64     int i, tableIndex;
65 
66     /*
67      * Allocate for optimism: one slot per entry, plus an end-of-list marker.
68      */
69     table = malloc(sizeof(InlineSub) * (count+1));
70 
71     tableIndex = 0;
72     for (i = 0; i < count; i++) {
73         clazz = dvmFindClassNoInit(ops[i].classDescriptor, NULL);
74         if (clazz == NULL) {
75             LOGV("DexOpt: can't inline for class '%s': not found\n",
76                 ops[i].classDescriptor);
77             dvmClearOptException(dvmThreadSelf());
78         } else {
79             /*
80              * Method could be virtual or direct.  Try both.  Don't use
81              * the "hier" versions.
82              */
83             method = dvmFindDirectMethodByDescriptor(clazz, ops[i].methodName,
84                         ops[i].methodSignature);
85             if (method == NULL)
86                 method = dvmFindVirtualMethodByDescriptor(clazz, ops[i].methodName,
87                         ops[i].methodSignature);
88             if (method == NULL) {
89                 LOGW("DexOpt: can't inline %s.%s %s: method not found\n",
90                     ops[i].classDescriptor, ops[i].methodName,
91                     ops[i].methodSignature);
92             } else {
93                 if (!dvmIsFinalClass(clazz) && !dvmIsFinalMethod(method)) {
94                     LOGW("DexOpt: WARNING: inline op on non-final class/method "
95                          "%s.%s\n",
96                         clazz->descriptor, method->name);
97                     /* fail? */
98                 }
99                 if (dvmIsSynchronizedMethod(method) ||
100                     dvmIsDeclaredSynchronizedMethod(method))
101                 {
102                     LOGW("DexOpt: WARNING: inline op on synchronized method "
103                          "%s.%s\n",
104                         clazz->descriptor, method->name);
105                     /* fail? */
106                 }
107 
108                 table[tableIndex].method = method;
109                 table[tableIndex].inlineIdx = i;
110                 tableIndex++;
111 
112                 LOGV("DexOpt: will inline %d: %s.%s %s\n", i,
113                     ops[i].classDescriptor, ops[i].methodName,
114                     ops[i].methodSignature);
115             }
116         }
117     }
118 
119     /* mark end of table */
120     table[tableIndex].method = NULL;
121     LOGV("DexOpt: inline table has %d entries\n", tableIndex);
122 
123     return table;
124 }
125 
126 /*
127  * Release inline sub data structure.
128  */
dvmFreeInlineSubsTable(InlineSub * inlineSubs)129 void dvmFreeInlineSubsTable(InlineSub* inlineSubs)
130 {
131     free(inlineSubs);
132 }
133 
134 
135 /*
136  * Optimize the specified class.
137  *
138  * If "essentialOnly" is true, we only do essential optimizations.  For
139  * example, accesses to volatile 64-bit fields must be replaced with
140  * "-wide-volatile" instructions or the program could behave incorrectly.
141  * (Skipping non-essential optimizations makes us a little bit faster, and
142  * more importantly avoids dirtying DEX pages.)
143  */
dvmOptimizeClass(ClassObject * clazz,bool essentialOnly)144 void dvmOptimizeClass(ClassObject* clazz, bool essentialOnly)
145 {
146     int i;
147 
148     for (i = 0; i < clazz->directMethodCount; i++) {
149         optimizeMethod(&clazz->directMethods[i], essentialOnly);
150     }
151     for (i = 0; i < clazz->virtualMethodCount; i++) {
152         optimizeMethod(&clazz->virtualMethods[i], essentialOnly);
153     }
154 }
155 
156 /*
157  * Optimize instructions in a method.
158  *
159  * This does a single pass through the code, examining each instruction.
160  *
161  * This is not expected to fail if the class was successfully verified.
162  * The only significant failure modes occur when an "essential" update fails,
163  * but we can't generally identify those: if we can't look up a field,
164  * we can't know if the field access was supposed to be handled as volatile.
165  *
166  * Instead, we give it our best effort, and hope for the best.  For 100%
167  * reliability, only optimize a class after verification succeeds.
168  */
optimizeMethod(Method * method,bool essentialOnly)169 static void optimizeMethod(Method* method, bool essentialOnly)
170 {
171     u4 insnsSize;
172     u2* insns;
173     u2 inst;
174 
175     if (!gDvm.optimizing && !essentialOnly) {
176         /* unexpected; will force copy-on-write of a lot of pages */
177         LOGD("NOTE: doing full bytecode optimization outside dexopt\n");
178     }
179 
180     if (dvmIsNativeMethod(method) || dvmIsAbstractMethod(method))
181         return;
182 
183     insns = (u2*) method->insns;
184     assert(insns != NULL);
185     insnsSize = dvmGetMethodInsnsSize(method);
186 
187     while (insnsSize > 0) {
188         OpCode quickOpc, volatileOpc = OP_NOP;
189         int width;
190         bool notMatched = false;
191 
192         inst = *insns & 0xff;
193 
194         /* "essential" substitutions, always checked */
195         switch (inst) {
196         case OP_IGET:
197         case OP_IGET_BOOLEAN:
198         case OP_IGET_BYTE:
199         case OP_IGET_CHAR:
200         case OP_IGET_SHORT:
201             quickOpc = OP_IGET_QUICK;
202             if (gDvm.dexOptForSmp)
203                 volatileOpc = OP_IGET_VOLATILE;
204             goto rewrite_inst_field;
205         case OP_IGET_WIDE:
206             quickOpc = OP_IGET_WIDE_QUICK;
207             volatileOpc = OP_IGET_WIDE_VOLATILE;
208             goto rewrite_inst_field;
209         case OP_IGET_OBJECT:
210             quickOpc = OP_IGET_OBJECT_QUICK;
211             if (gDvm.dexOptForSmp)
212                 volatileOpc = OP_IGET_OBJECT_VOLATILE;
213             goto rewrite_inst_field;
214         case OP_IPUT:
215         case OP_IPUT_BOOLEAN:
216         case OP_IPUT_BYTE:
217         case OP_IPUT_CHAR:
218         case OP_IPUT_SHORT:
219             quickOpc = OP_IPUT_QUICK;
220             if (gDvm.dexOptForSmp)
221                 volatileOpc = OP_IPUT_VOLATILE;
222             goto rewrite_inst_field;
223         case OP_IPUT_WIDE:
224             quickOpc = OP_IPUT_WIDE_QUICK;
225             volatileOpc = OP_IPUT_WIDE_VOLATILE;
226             goto rewrite_inst_field;
227         case OP_IPUT_OBJECT:
228             quickOpc = OP_IPUT_OBJECT_QUICK;
229             if (gDvm.dexOptForSmp)
230                 volatileOpc = OP_IPUT_OBJECT_VOLATILE;
231 rewrite_inst_field:
232             if (essentialOnly)
233                 quickOpc = OP_NOP;
234             if (quickOpc != OP_NOP || volatileOpc != OP_NOP)
235                 rewriteInstField(method, insns, quickOpc, volatileOpc);
236             break;
237 
238         case OP_SGET_WIDE:
239             volatileOpc = OP_SGET_WIDE_VOLATILE;
240             goto rewrite_static_field;
241         case OP_SPUT_WIDE:
242             volatileOpc = OP_SPUT_WIDE_VOLATILE;
243 rewrite_static_field:
244             rewriteStaticField(method, insns, volatileOpc);
245             break;
246         default:
247             notMatched = true;
248             break;
249         }
250 
251         if (notMatched && gDvm.dexOptForSmp) {
252             /* additional "essential" substitutions for an SMP device */
253             switch (inst) {
254             case OP_SGET:
255             case OP_SGET_BOOLEAN:
256             case OP_SGET_BYTE:
257             case OP_SGET_CHAR:
258             case OP_SGET_SHORT:
259                 volatileOpc = OP_SGET_VOLATILE;
260                 goto rewrite_static_field2;
261             case OP_SGET_OBJECT:
262                 volatileOpc = OP_SGET_OBJECT_VOLATILE;
263                 goto rewrite_static_field2;
264             case OP_SPUT:
265             case OP_SPUT_BOOLEAN:
266             case OP_SPUT_BYTE:
267             case OP_SPUT_CHAR:
268             case OP_SPUT_SHORT:
269                 volatileOpc = OP_SPUT_VOLATILE;
270                 goto rewrite_static_field2;
271             case OP_SPUT_OBJECT:
272                 volatileOpc = OP_SPUT_OBJECT_VOLATILE;
273 rewrite_static_field2:
274                 rewriteStaticField(method, insns, volatileOpc);
275                 notMatched = false;
276                 break;
277             default:
278                 assert(notMatched);
279                 break;
280             }
281         }
282 
283         /* non-essential substitutions */
284         if (notMatched && !essentialOnly) {
285             switch (inst) {
286             case OP_INVOKE_VIRTUAL:
287                 if (!rewriteExecuteInline(method, insns, METHOD_VIRTUAL)) {
288                     rewriteVirtualInvoke(method, insns,
289                             OP_INVOKE_VIRTUAL_QUICK);
290                 }
291                 break;
292             case OP_INVOKE_VIRTUAL_RANGE:
293                 if (!rewriteExecuteInlineRange(method, insns, METHOD_VIRTUAL)) {
294                     rewriteVirtualInvoke(method, insns,
295                             OP_INVOKE_VIRTUAL_QUICK_RANGE);
296                 }
297                 break;
298             case OP_INVOKE_SUPER:
299                 rewriteVirtualInvoke(method, insns, OP_INVOKE_SUPER_QUICK);
300                 break;
301             case OP_INVOKE_SUPER_RANGE:
302                 rewriteVirtualInvoke(method, insns, OP_INVOKE_SUPER_QUICK_RANGE);
303                 break;
304 
305             case OP_INVOKE_DIRECT:
306                 if (!rewriteExecuteInline(method, insns, METHOD_DIRECT)) {
307                     rewriteEmptyDirectInvoke(method, insns);
308                 }
309                 break;
310             case OP_INVOKE_DIRECT_RANGE:
311                 rewriteExecuteInlineRange(method, insns, METHOD_DIRECT);
312                 break;
313 
314             case OP_INVOKE_STATIC:
315                 rewriteExecuteInline(method, insns, METHOD_STATIC);
316                 break;
317             case OP_INVOKE_STATIC_RANGE:
318                 rewriteExecuteInlineRange(method, insns, METHOD_STATIC);
319                 break;
320 
321             default:
322                 /* nothing to do for this instruction */
323                 ;
324             }
325         }
326 
327         width = dexGetInstrOrTableWidthAbs(gDvm.instrWidth, insns);
328         assert(width > 0);
329 
330         insns += width;
331         insnsSize -= width;
332     }
333 
334     assert(insnsSize == 0);
335 }
336 
337 /*
338  * Update a 16-bit code unit in "meth".
339  */
updateCode(const Method * meth,u2 * ptr,u2 newVal)340 static inline void updateCode(const Method* meth, u2* ptr, u2 newVal)
341 {
342     if (gDvm.optimizing) {
343         /* dexopt time, alter the output directly */
344         *ptr = newVal;
345     } else {
346         /* runtime, toggle the page read/write status */
347         dvmDexChangeDex2(meth->clazz->pDvmDex, ptr, newVal);
348     }
349 }
350 
351 /*
352  * If "referrer" and "resClass" don't come from the same DEX file, and
353  * the DEX we're working on is not destined for the bootstrap class path,
354  * tweak the class loader so package-access checks work correctly.
355  *
356  * Only do this if we're doing pre-verification or optimization.
357  */
tweakLoader(ClassObject * referrer,ClassObject * resClass)358 static void tweakLoader(ClassObject* referrer, ClassObject* resClass)
359 {
360     if (!gDvm.optimizing)
361         return;
362     assert(referrer->classLoader == NULL);
363     assert(resClass->classLoader == NULL);
364 
365     if (!gDvm.optimizingBootstrapClass) {
366         /* class loader for an array class comes from element type */
367         if (dvmIsArrayClass(resClass))
368             resClass = resClass->elementClass;
369         if (referrer->pDvmDex != resClass->pDvmDex)
370             resClass->classLoader = (Object*) 0xdead3333;
371     }
372 }
373 
374 /*
375  * Undo the effects of tweakLoader.
376  */
untweakLoader(ClassObject * referrer,ClassObject * resClass)377 static void untweakLoader(ClassObject* referrer, ClassObject* resClass)
378 {
379     if (!gDvm.optimizing || gDvm.optimizingBootstrapClass)
380         return;
381 
382     if (dvmIsArrayClass(resClass))
383         resClass = resClass->elementClass;
384     resClass->classLoader = NULL;
385 }
386 
387 
388 /*
389  * Alternate version of dvmResolveClass for use with verification and
390  * optimization.  Performs access checks on every resolve, and refuses
391  * to acknowledge the existence of classes defined in more than one DEX
392  * file.
393  *
394  * Exceptions caused by failures are cleared before returning.
395  *
396  * On failure, returns NULL, and sets *pFailure if pFailure is not NULL.
397  */
dvmOptResolveClass(ClassObject * referrer,u4 classIdx,VerifyError * pFailure)398 ClassObject* dvmOptResolveClass(ClassObject* referrer, u4 classIdx,
399     VerifyError* pFailure)
400 {
401     DvmDex* pDvmDex = referrer->pDvmDex;
402     ClassObject* resClass;
403 
404     /*
405      * Check the table first.  If not there, do the lookup by name.
406      */
407     resClass = dvmDexGetResolvedClass(pDvmDex, classIdx);
408     if (resClass == NULL) {
409         const char* className = dexStringByTypeIdx(pDvmDex->pDexFile, classIdx);
410         if (className[0] != '\0' && className[1] == '\0') {
411             /* primitive type */
412             resClass = dvmFindPrimitiveClass(className[0]);
413         } else {
414             resClass = dvmFindClassNoInit(className, referrer->classLoader);
415         }
416         if (resClass == NULL) {
417             /* not found, exception should be raised */
418             LOGV("DexOpt: class %d (%s) not found\n",
419                 classIdx,
420                 dexStringByTypeIdx(pDvmDex->pDexFile, classIdx));
421             if (pFailure != NULL) {
422                 /* dig through the wrappers to find the original failure */
423                 Object* excep = dvmGetException(dvmThreadSelf());
424                 while (true) {
425                     Object* cause = dvmGetExceptionCause(excep);
426                     if (cause == NULL)
427                         break;
428                     excep = cause;
429                 }
430                 if (strcmp(excep->clazz->descriptor,
431                     "Ljava/lang/IncompatibleClassChangeError;") == 0)
432                 {
433                     *pFailure = VERIFY_ERROR_CLASS_CHANGE;
434                 } else {
435                     *pFailure = VERIFY_ERROR_NO_CLASS;
436                 }
437             }
438             dvmClearOptException(dvmThreadSelf());
439             return NULL;
440         }
441 
442         /*
443          * Add it to the resolved table so we're faster on the next lookup.
444          */
445         dvmDexSetResolvedClass(pDvmDex, classIdx, resClass);
446     }
447 
448     /* multiple definitions? */
449     if (IS_CLASS_FLAG_SET(resClass, CLASS_MULTIPLE_DEFS)) {
450         LOGI("DexOpt: not resolving ambiguous class '%s'\n",
451             resClass->descriptor);
452         if (pFailure != NULL)
453             *pFailure = VERIFY_ERROR_NO_CLASS;
454         return NULL;
455     }
456 
457     /* access allowed? */
458     tweakLoader(referrer, resClass);
459     bool allowed = dvmCheckClassAccess(referrer, resClass);
460     untweakLoader(referrer, resClass);
461     if (!allowed) {
462         LOGW("DexOpt: resolve class illegal access: %s -> %s\n",
463             referrer->descriptor, resClass->descriptor);
464         if (pFailure != NULL)
465             *pFailure = VERIFY_ERROR_ACCESS_CLASS;
466         return NULL;
467     }
468 
469     return resClass;
470 }
471 
472 /*
473  * Alternate version of dvmResolveInstField().
474  *
475  * On failure, returns NULL, and sets *pFailure if pFailure is not NULL.
476  */
dvmOptResolveInstField(ClassObject * referrer,u4 ifieldIdx,VerifyError * pFailure)477 InstField* dvmOptResolveInstField(ClassObject* referrer, u4 ifieldIdx,
478     VerifyError* pFailure)
479 {
480     DvmDex* pDvmDex = referrer->pDvmDex;
481     InstField* resField;
482 
483     resField = (InstField*) dvmDexGetResolvedField(pDvmDex, ifieldIdx);
484     if (resField == NULL) {
485         const DexFieldId* pFieldId;
486         ClassObject* resClass;
487 
488         pFieldId = dexGetFieldId(pDvmDex->pDexFile, ifieldIdx);
489 
490         /*
491          * Find the field's class.
492          */
493         resClass = dvmOptResolveClass(referrer, pFieldId->classIdx, pFailure);
494         if (resClass == NULL) {
495             //dvmClearOptException(dvmThreadSelf());
496             assert(!dvmCheckException(dvmThreadSelf()));
497             if (pFailure != NULL) { assert(!VERIFY_OK(*pFailure)); }
498             return NULL;
499         }
500 
501         resField = (InstField*)dvmFindFieldHier(resClass,
502             dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx),
503             dexStringByTypeIdx(pDvmDex->pDexFile, pFieldId->typeIdx));
504         if (resField == NULL) {
505             LOGD("DexOpt: couldn't find field %s.%s\n",
506                 resClass->descriptor,
507                 dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx));
508             if (pFailure != NULL)
509                 *pFailure = VERIFY_ERROR_NO_FIELD;
510             return NULL;
511         }
512         if (dvmIsStaticField(&resField->field)) {
513             LOGD("DexOpt: wanted instance, got static for field %s.%s\n",
514                 resClass->descriptor,
515                 dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx));
516             if (pFailure != NULL)
517                 *pFailure = VERIFY_ERROR_CLASS_CHANGE;
518             return NULL;
519         }
520 
521         /*
522          * Add it to the resolved table so we're faster on the next lookup.
523          */
524         dvmDexSetResolvedField(pDvmDex, ifieldIdx, (Field*) resField);
525     }
526 
527     /* access allowed? */
528     tweakLoader(referrer, resField->field.clazz);
529     bool allowed = dvmCheckFieldAccess(referrer, (Field*)resField);
530     untweakLoader(referrer, resField->field.clazz);
531     if (!allowed) {
532         LOGI("DexOpt: access denied from %s to field %s.%s\n",
533             referrer->descriptor, resField->field.clazz->descriptor,
534             resField->field.name);
535         if (pFailure != NULL)
536             *pFailure = VERIFY_ERROR_ACCESS_FIELD;
537         return NULL;
538     }
539 
540     return resField;
541 }
542 
543 /*
544  * Alternate version of dvmResolveStaticField().
545  *
546  * Does not force initialization of the resolved field's class.
547  *
548  * On failure, returns NULL, and sets *pFailure if pFailure is not NULL.
549  */
dvmOptResolveStaticField(ClassObject * referrer,u4 sfieldIdx,VerifyError * pFailure)550 StaticField* dvmOptResolveStaticField(ClassObject* referrer, u4 sfieldIdx,
551     VerifyError* pFailure)
552 {
553     DvmDex* pDvmDex = referrer->pDvmDex;
554     StaticField* resField;
555 
556     resField = (StaticField*)dvmDexGetResolvedField(pDvmDex, sfieldIdx);
557     if (resField == NULL) {
558         const DexFieldId* pFieldId;
559         ClassObject* resClass;
560 
561         pFieldId = dexGetFieldId(pDvmDex->pDexFile, sfieldIdx);
562 
563         /*
564          * Find the field's class.
565          */
566         resClass = dvmOptResolveClass(referrer, pFieldId->classIdx, pFailure);
567         if (resClass == NULL) {
568             //dvmClearOptException(dvmThreadSelf());
569             assert(!dvmCheckException(dvmThreadSelf()));
570             if (pFailure != NULL) { assert(!VERIFY_OK(*pFailure)); }
571             return NULL;
572         }
573 
574         resField = (StaticField*)dvmFindFieldHier(resClass,
575                     dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx),
576                     dexStringByTypeIdx(pDvmDex->pDexFile, pFieldId->typeIdx));
577         if (resField == NULL) {
578             LOGD("DexOpt: couldn't find static field\n");
579             if (pFailure != NULL)
580                 *pFailure = VERIFY_ERROR_NO_FIELD;
581             return NULL;
582         }
583         if (!dvmIsStaticField(&resField->field)) {
584             LOGD("DexOpt: wanted static, got instance for field %s.%s\n",
585                 resClass->descriptor,
586                 dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx));
587             if (pFailure != NULL)
588                 *pFailure = VERIFY_ERROR_CLASS_CHANGE;
589             return NULL;
590         }
591 
592         /*
593          * Add it to the resolved table so we're faster on the next lookup.
594          *
595          * We can only do this if we're in "dexopt", because the presence
596          * of a valid value in the resolution table implies that the class
597          * containing the static field has been initialized.
598          */
599         if (gDvm.optimizing)
600             dvmDexSetResolvedField(pDvmDex, sfieldIdx, (Field*) resField);
601     }
602 
603     /* access allowed? */
604     tweakLoader(referrer, resField->field.clazz);
605     bool allowed = dvmCheckFieldAccess(referrer, (Field*)resField);
606     untweakLoader(referrer, resField->field.clazz);
607     if (!allowed) {
608         LOGI("DexOpt: access denied from %s to field %s.%s\n",
609             referrer->descriptor, resField->field.clazz->descriptor,
610             resField->field.name);
611         if (pFailure != NULL)
612             *pFailure = VERIFY_ERROR_ACCESS_FIELD;
613         return NULL;
614     }
615 
616     return resField;
617 }
618 
619 
620 /*
621  * Rewrite an iget/iput instruction.  These all have the form:
622  *   op vA, vB, field@CCCC
623  *
624  * Where vA holds the value, vB holds the object reference, and CCCC is
625  * the field reference constant pool offset.  For a non-volatile field,
626  * we want to replace the opcode with "quickOpc" and replace CCCC with
627  * the byte offset from the start of the object.  For a volatile field,
628  * we just want to replace the opcode with "volatileOpc".
629  *
630  * If "volatileOpc" is OP_NOP we don't check to see if it's a volatile
631  * field.  If "quickOpc" is OP_NOP, and this is a non-volatile field,
632  * we don't do anything.
633  *
634  * "method" is the referring method.
635  */
rewriteInstField(Method * method,u2 * insns,OpCode quickOpc,OpCode volatileOpc)636 static bool rewriteInstField(Method* method, u2* insns, OpCode quickOpc,
637     OpCode volatileOpc)
638 {
639     ClassObject* clazz = method->clazz;
640     u2 fieldIdx = insns[1];
641     InstField* instField;
642 
643     instField = dvmOptResolveInstField(clazz, fieldIdx, NULL);
644     if (instField == NULL) {
645         LOGI("DexOpt: unable to optimize instance field ref "
646              "0x%04x at 0x%02x in %s.%s\n",
647             fieldIdx, (int) (insns - method->insns), clazz->descriptor,
648             method->name);
649         return false;
650     }
651 
652     if (instField->byteOffset >= 65536) {
653         LOGI("DexOpt: field offset exceeds 64K (%d)\n", instField->byteOffset);
654         return false;
655     }
656 
657     if (volatileOpc != OP_NOP && dvmIsVolatileField(&instField->field)) {
658         updateCode(method, insns, (insns[0] & 0xff00) | (u2) volatileOpc);
659         LOGV("DexOpt: rewrote ifield access %s.%s --> volatile\n",
660             instField->field.clazz->descriptor, instField->field.name);
661     } else if (quickOpc != OP_NOP) {
662         updateCode(method, insns, (insns[0] & 0xff00) | (u2) quickOpc);
663         updateCode(method, insns+1, (u2) instField->byteOffset);
664         LOGV("DexOpt: rewrote ifield access %s.%s --> %d\n",
665             instField->field.clazz->descriptor, instField->field.name,
666             instField->byteOffset);
667     } else {
668         LOGV("DexOpt: no rewrite of ifield access %s.%s\n",
669             instField->field.clazz->descriptor, instField->field.name);
670     }
671 
672     return true;
673 }
674 
675 /*
676  * Rewrite an sget/sput instruction.  These all have the form:
677  *   op vAA, field@BBBB
678  *
679  * Where vAA holds the value, and BBBB is the field reference constant
680  * pool offset.  There is no "quick" form of static field accesses, so
681  * this is only useful for volatile fields.
682  *
683  * "method" is the referring method.
684  */
rewriteStaticField(Method * method,u2 * insns,OpCode volatileOpc)685 static bool rewriteStaticField(Method* method, u2* insns, OpCode volatileOpc)
686 {
687     ClassObject* clazz = method->clazz;
688     u2 fieldIdx = insns[1];
689     StaticField* staticField;
690 
691     assert(volatileOpc != OP_NOP);
692 
693     staticField = dvmOptResolveStaticField(clazz, fieldIdx, NULL);
694     if (staticField == NULL) {
695         LOGI("DexOpt: unable to optimize static field ref "
696              "0x%04x at 0x%02x in %s.%s\n",
697             fieldIdx, (int) (insns - method->insns), clazz->descriptor,
698             method->name);
699         return false;
700     }
701 
702     if (dvmIsVolatileField(&staticField->field)) {
703         updateCode(method, insns, (insns[0] & 0xff00) | (u2) volatileOpc);
704         LOGV("DexOpt: rewrote sfield access %s.%s --> volatile\n",
705             staticField->field.clazz->descriptor, staticField->field.name);
706     }
707 
708     return true;
709 }
710 
711 /*
712  * Alternate version of dvmResolveMethod().
713  *
714  * Doesn't throw exceptions, and checks access on every lookup.
715  *
716  * On failure, returns NULL, and sets *pFailure if pFailure is not NULL.
717  */
dvmOptResolveMethod(ClassObject * referrer,u4 methodIdx,MethodType methodType,VerifyError * pFailure)718 Method* dvmOptResolveMethod(ClassObject* referrer, u4 methodIdx,
719     MethodType methodType, VerifyError* pFailure)
720 {
721     DvmDex* pDvmDex = referrer->pDvmDex;
722     Method* resMethod;
723 
724     assert(methodType == METHOD_DIRECT ||
725            methodType == METHOD_VIRTUAL ||
726            methodType == METHOD_STATIC);
727 
728     LOGVV("--- resolving method %u (referrer=%s)\n", methodIdx,
729         referrer->descriptor);
730 
731     resMethod = dvmDexGetResolvedMethod(pDvmDex, methodIdx);
732     if (resMethod == NULL) {
733         const DexMethodId* pMethodId;
734         ClassObject* resClass;
735 
736         pMethodId = dexGetMethodId(pDvmDex->pDexFile, methodIdx);
737 
738         resClass = dvmOptResolveClass(referrer, pMethodId->classIdx, pFailure);
739         if (resClass == NULL) {
740             /*
741              * Can't find the class that the method is a part of, or don't
742              * have permission to access the class.
743              */
744             LOGV("DexOpt: can't find called method's class (?.%s)\n",
745                 dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx));
746             if (pFailure != NULL) { assert(!VERIFY_OK(*pFailure)); }
747             return NULL;
748         }
749         if (dvmIsInterfaceClass(resClass)) {
750             /* method is part of an interface; this is wrong method for that */
751             LOGW("DexOpt: method is in an interface\n");
752             if (pFailure != NULL)
753                 *pFailure = VERIFY_ERROR_GENERIC;
754             return NULL;
755         }
756 
757         /*
758          * We need to chase up the class hierarchy to find methods defined
759          * in super-classes.  (We only want to check the current class
760          * if we're looking for a constructor.)
761          */
762         DexProto proto;
763         dexProtoSetFromMethodId(&proto, pDvmDex->pDexFile, pMethodId);
764 
765         if (methodType == METHOD_DIRECT) {
766             resMethod = dvmFindDirectMethod(resClass,
767                 dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx), &proto);
768         } else {
769             /* METHOD_STATIC or METHOD_VIRTUAL */
770             resMethod = dvmFindMethodHier(resClass,
771                 dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx), &proto);
772         }
773 
774         if (resMethod == NULL) {
775             LOGV("DexOpt: couldn't find method '%s'\n",
776                 dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx));
777             if (pFailure != NULL)
778                 *pFailure = VERIFY_ERROR_NO_METHOD;
779             return NULL;
780         }
781         if (methodType == METHOD_STATIC) {
782             if (!dvmIsStaticMethod(resMethod)) {
783                 LOGD("DexOpt: wanted static, got instance for method %s.%s\n",
784                     resClass->descriptor, resMethod->name);
785                 if (pFailure != NULL)
786                     *pFailure = VERIFY_ERROR_CLASS_CHANGE;
787                 return NULL;
788             }
789         } else if (methodType == METHOD_VIRTUAL) {
790             if (dvmIsStaticMethod(resMethod)) {
791                 LOGD("DexOpt: wanted instance, got static for method %s.%s\n",
792                     resClass->descriptor, resMethod->name);
793                 if (pFailure != NULL)
794                     *pFailure = VERIFY_ERROR_CLASS_CHANGE;
795                 return NULL;
796             }
797         }
798 
799         /* see if this is a pure-abstract method */
800         if (dvmIsAbstractMethod(resMethod) && !dvmIsAbstractClass(resClass)) {
801             LOGW("DexOpt: pure-abstract method '%s' in %s\n",
802                 dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx),
803                 resClass->descriptor);
804             if (pFailure != NULL)
805                 *pFailure = VERIFY_ERROR_GENERIC;
806             return NULL;
807         }
808 
809         /*
810          * Add it to the resolved table so we're faster on the next lookup.
811          *
812          * We can only do this for static methods if we're not in "dexopt",
813          * because the presence of a valid value in the resolution table
814          * implies that the class containing the static field has been
815          * initialized.
816          */
817         if (methodType != METHOD_STATIC || gDvm.optimizing)
818             dvmDexSetResolvedMethod(pDvmDex, methodIdx, resMethod);
819     }
820 
821     LOGVV("--- found method %d (%s.%s)\n",
822         methodIdx, resMethod->clazz->descriptor, resMethod->name);
823 
824     /* access allowed? */
825     tweakLoader(referrer, resMethod->clazz);
826     bool allowed = dvmCheckMethodAccess(referrer, resMethod);
827     untweakLoader(referrer, resMethod->clazz);
828     if (!allowed) {
829         IF_LOGI() {
830             char* desc = dexProtoCopyMethodDescriptor(&resMethod->prototype);
831             LOGI("DexOpt: illegal method access (call %s.%s %s from %s)\n",
832                 resMethod->clazz->descriptor, resMethod->name, desc,
833                 referrer->descriptor);
834             free(desc);
835         }
836         if (pFailure != NULL)
837             *pFailure = VERIFY_ERROR_ACCESS_METHOD;
838         return NULL;
839     }
840 
841     return resMethod;
842 }
843 
844 /*
845  * Rewrite invoke-virtual, invoke-virtual/range, invoke-super, and
846  * invoke-super/range.  These all have the form:
847  *   op vAA, meth@BBBB, reg stuff @CCCC
848  *
849  * We want to replace the method constant pool index BBBB with the
850  * vtable index.
851  */
rewriteVirtualInvoke(Method * method,u2 * insns,OpCode newOpc)852 static bool rewriteVirtualInvoke(Method* method, u2* insns, OpCode newOpc)
853 {
854     ClassObject* clazz = method->clazz;
855     Method* baseMethod;
856     u2 methodIdx = insns[1];
857 
858     baseMethod = dvmOptResolveMethod(clazz, methodIdx, METHOD_VIRTUAL, NULL);
859     if (baseMethod == NULL) {
860         LOGD("DexOpt: unable to optimize virt call 0x%04x at 0x%02x in %s.%s\n",
861             methodIdx,
862             (int) (insns - method->insns), clazz->descriptor,
863             method->name);
864         return false;
865     }
866 
867     assert((insns[0] & 0xff) == OP_INVOKE_VIRTUAL ||
868            (insns[0] & 0xff) == OP_INVOKE_VIRTUAL_RANGE ||
869            (insns[0] & 0xff) == OP_INVOKE_SUPER ||
870            (insns[0] & 0xff) == OP_INVOKE_SUPER_RANGE);
871 
872     /*
873      * Note: Method->methodIndex is a u2 and is range checked during the
874      * initial load.
875      */
876     updateCode(method, insns, (insns[0] & 0xff00) | (u2) newOpc);
877     updateCode(method, insns+1, baseMethod->methodIndex);
878 
879     //LOGI("DexOpt: rewrote call to %s.%s --> %s.%s\n",
880     //    method->clazz->descriptor, method->name,
881     //    baseMethod->clazz->descriptor, baseMethod->name);
882 
883     return true;
884 }
885 
886 /*
887  * Rewrite invoke-direct, which has the form:
888  *   op vAA, meth@BBBB, reg stuff @CCCC
889  *
890  * There isn't a lot we can do to make this faster, but in some situations
891  * we can make it go away entirely.
892  *
893  * This must only be used when the invoked method does nothing and has
894  * no return value (the latter being very important for verification).
895  */
rewriteEmptyDirectInvoke(Method * method,u2 * insns)896 static bool rewriteEmptyDirectInvoke(Method* method, u2* insns)
897 {
898     ClassObject* clazz = method->clazz;
899     Method* calledMethod;
900     u2 methodIdx = insns[1];
901 
902     calledMethod = dvmOptResolveMethod(clazz, methodIdx, METHOD_DIRECT, NULL);
903     if (calledMethod == NULL) {
904         LOGD("DexOpt: unable to opt direct call 0x%04x at 0x%02x in %s.%s\n",
905             methodIdx,
906             (int) (insns - method->insns), clazz->descriptor,
907             method->name);
908         return false;
909     }
910 
911     /* TODO: verify that java.lang.Object() is actually empty! */
912     if (calledMethod->clazz == gDvm.classJavaLangObject &&
913         dvmCompareNameDescriptorAndMethod("<init>", "()V", calledMethod) == 0)
914     {
915         /*
916          * Replace with "empty" instruction.  DO NOT disturb anything
917          * else about it, as we want it to function the same as
918          * OP_INVOKE_DIRECT when debugging is enabled.
919          */
920         assert((insns[0] & 0xff) == OP_INVOKE_DIRECT);
921         updateCode(method, insns,
922             (insns[0] & 0xff00) | (u2) OP_INVOKE_DIRECT_EMPTY);
923 
924         //LOGI("DexOpt: marked-empty call to %s.%s --> %s.%s\n",
925         //    method->clazz->descriptor, method->name,
926         //    calledMethod->clazz->descriptor, calledMethod->name);
927     }
928 
929     return true;
930 }
931 
932 /*
933  * Resolve an interface method reference.
934  *
935  * No method access check here -- interface methods are always public.
936  *
937  * Returns NULL if the method was not found.  Does not throw an exception.
938  */
dvmOptResolveInterfaceMethod(ClassObject * referrer,u4 methodIdx)939 Method* dvmOptResolveInterfaceMethod(ClassObject* referrer, u4 methodIdx)
940 {
941     DvmDex* pDvmDex = referrer->pDvmDex;
942     Method* resMethod;
943     int i;
944 
945     LOGVV("--- resolving interface method %d (referrer=%s)\n",
946         methodIdx, referrer->descriptor);
947 
948     resMethod = dvmDexGetResolvedMethod(pDvmDex, methodIdx);
949     if (resMethod == NULL) {
950         const DexMethodId* pMethodId;
951         ClassObject* resClass;
952 
953         pMethodId = dexGetMethodId(pDvmDex->pDexFile, methodIdx);
954 
955         resClass = dvmOptResolveClass(referrer, pMethodId->classIdx, NULL);
956         if (resClass == NULL) {
957             /* can't find the class that the method is a part of */
958             dvmClearOptException(dvmThreadSelf());
959             return NULL;
960         }
961         if (!dvmIsInterfaceClass(resClass)) {
962             /* whoops */
963             LOGI("Interface method not part of interface class\n");
964             return NULL;
965         }
966 
967         const char* methodName =
968             dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx);
969         DexProto proto;
970         dexProtoSetFromMethodId(&proto, pDvmDex->pDexFile, pMethodId);
971 
972         LOGVV("+++ looking for '%s' '%s' in resClass='%s'\n",
973             methodName, methodSig, resClass->descriptor);
974         resMethod = dvmFindVirtualMethod(resClass, methodName, &proto);
975         if (resMethod == NULL) {
976             /* scan superinterfaces and superclass interfaces */
977             LOGVV("+++ did not resolve immediately\n");
978             for (i = 0; i < resClass->iftableCount; i++) {
979                 resMethod = dvmFindVirtualMethod(resClass->iftable[i].clazz,
980                                 methodName, &proto);
981                 if (resMethod != NULL)
982                     break;
983             }
984 
985             if (resMethod == NULL) {
986                 LOGVV("+++ unable to resolve method %s\n", methodName);
987                 return NULL;
988             }
989         } else {
990             LOGVV("+++ resolved immediately: %s (%s %d)\n", resMethod->name,
991                 resMethod->clazz->descriptor, (u4) resMethod->methodIndex);
992         }
993 
994         /* we're expecting this to be abstract */
995         if (!dvmIsAbstractMethod(resMethod)) {
996             char* desc = dexProtoCopyMethodDescriptor(&resMethod->prototype);
997             LOGW("Found non-abstract interface method %s.%s %s\n",
998                 resMethod->clazz->descriptor, resMethod->name, desc);
999             free(desc);
1000             return NULL;
1001         }
1002 
1003         /*
1004          * Add it to the resolved table so we're faster on the next lookup.
1005          */
1006         dvmDexSetResolvedMethod(pDvmDex, methodIdx, resMethod);
1007     }
1008 
1009     LOGVV("--- found interface method %d (%s.%s)\n",
1010         methodIdx, resMethod->clazz->descriptor, resMethod->name);
1011 
1012     /* interface methods are always public; no need to check access */
1013 
1014     return resMethod;
1015 }
1016 
1017 /*
1018  * See if the method being called can be rewritten as an inline operation.
1019  * Works for invoke-virtual, invoke-direct, and invoke-static.
1020  *
1021  * Returns "true" if we replace it.
1022  */
rewriteExecuteInline(Method * method,u2 * insns,MethodType methodType)1023 static bool rewriteExecuteInline(Method* method, u2* insns,
1024     MethodType methodType)
1025 {
1026     const InlineSub* inlineSubs = gDvm.inlineSubs;
1027     ClassObject* clazz = method->clazz;
1028     Method* calledMethod;
1029     u2 methodIdx = insns[1];
1030 
1031     //return false;
1032 
1033     calledMethod = dvmOptResolveMethod(clazz, methodIdx, methodType, NULL);
1034     if (calledMethod == NULL) {
1035         LOGV("+++ DexOpt inline: can't find %d\n", methodIdx);
1036         return false;
1037     }
1038 
1039     while (inlineSubs->method != NULL) {
1040         /*
1041         if (extra) {
1042             LOGI("comparing %p vs %p %s.%s %s\n",
1043                 inlineSubs->method, calledMethod,
1044                 inlineSubs->method->clazz->descriptor,
1045                 inlineSubs->method->name,
1046                 inlineSubs->method->signature);
1047         }
1048         */
1049         if (inlineSubs->method == calledMethod) {
1050             assert((insns[0] & 0xff) == OP_INVOKE_DIRECT ||
1051                    (insns[0] & 0xff) == OP_INVOKE_STATIC ||
1052                    (insns[0] & 0xff) == OP_INVOKE_VIRTUAL);
1053             updateCode(method, insns,
1054                 (insns[0] & 0xff00) | (u2) OP_EXECUTE_INLINE);
1055             updateCode(method, insns+1, (u2) inlineSubs->inlineIdx);
1056 
1057             //LOGI("DexOpt: execute-inline %s.%s --> %s.%s\n",
1058             //    method->clazz->descriptor, method->name,
1059             //    calledMethod->clazz->descriptor, calledMethod->name);
1060             return true;
1061         }
1062 
1063         inlineSubs++;
1064     }
1065 
1066     return false;
1067 }
1068 
1069 /*
1070  * See if the method being called can be rewritten as an inline operation.
1071  * Works for invoke-virtual/range, invoke-direct/range, and invoke-static/range.
1072  *
1073  * Returns "true" if we replace it.
1074  */
rewriteExecuteInlineRange(Method * method,u2 * insns,MethodType methodType)1075 static bool rewriteExecuteInlineRange(Method* method, u2* insns,
1076     MethodType methodType)
1077 {
1078     const InlineSub* inlineSubs = gDvm.inlineSubs;
1079     ClassObject* clazz = method->clazz;
1080     Method* calledMethod;
1081     u2 methodIdx = insns[1];
1082 
1083     calledMethod = dvmOptResolveMethod(clazz, methodIdx, methodType, NULL);
1084     if (calledMethod == NULL) {
1085         LOGV("+++ DexOpt inline/range: can't find %d\n", methodIdx);
1086         return false;
1087     }
1088 
1089     while (inlineSubs->method != NULL) {
1090         if (inlineSubs->method == calledMethod) {
1091             assert((insns[0] & 0xff) == OP_INVOKE_DIRECT_RANGE ||
1092                    (insns[0] & 0xff) == OP_INVOKE_STATIC_RANGE ||
1093                    (insns[0] & 0xff) == OP_INVOKE_VIRTUAL_RANGE);
1094             updateCode(method, insns,
1095                 (insns[0] & 0xff00) | (u2) OP_EXECUTE_INLINE_RANGE);
1096             updateCode(method, insns+1, (u2) inlineSubs->inlineIdx);
1097 
1098             //LOGI("DexOpt: execute-inline/range %s.%s --> %s.%s\n",
1099             //    method->clazz->descriptor, method->name,
1100             //    calledMethod->clazz->descriptor, calledMethod->name);
1101             return true;
1102         }
1103 
1104         inlineSubs++;
1105     }
1106 
1107     return false;
1108 }
1109