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