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