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