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