1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
|
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Resolve classes, methods, fields, and strings.
*
* According to the VM spec (v2 5.5), classes may be initialized by use
* of the "new", "getstatic", "putstatic", or "invokestatic" instructions.
* If we are resolving a static method or static field, we make the
* initialization check here.
*
* (NOTE: the verifier has its own resolve functions, which can be invoked
* if a class isn't pre-verified. Those functions must not update the
* "resolved stuff" tables for static fields and methods, because they do
* not perform initialization.)
*/
#include "Dalvik.h"
#include <stdlib.h>
/*
* Find the class corresponding to "classIdx", which maps to a class name
* string. It might be in the same DEX file as "referrer", in a different
* DEX file, generated by a class loader, or generated by the VM (e.g.
* array classes).
*
* Because the DexTypeId is associated with the referring class' DEX file,
* we may have to resolve the same class more than once if it's referred
* to from classes in multiple DEX files. This is a necessary property for
* DEX files associated with different class loaders.
*
* We cache a copy of the lookup in the DexFile's "resolved class" table,
* so future references to "classIdx" are faster.
*
* Note that "referrer" may be in the process of being linked.
*
* Traditional VMs might do access checks here, but in Dalvik the class
* "constant pool" is shared between all classes in the DEX file. We rely
* on the verifier to do the checks for us.
*
* Does not initialize the class.
*
* "fromUnverifiedConstant" should only be set if this call is the direct
* result of executing a "const-class" or "instance-of" instruction, which
* use class constants not resolved by the bytecode verifier.
*
* Returns NULL with an exception raised on failure.
*/
ClassObject* dvmResolveClass(const ClassObject* referrer, u4 classIdx,
bool fromUnverifiedConstant)
{
DvmDex* pDvmDex = referrer->pDvmDex;
ClassObject* resClass;
const char* className;
/*
* Check the table first -- this gets called from the other "resolve"
* methods.
*/
resClass = dvmDexGetResolvedClass(pDvmDex, classIdx);
if (resClass != NULL)
return resClass;
LOGVV("--- resolving class %u (referrer=%s cl=%p)",
classIdx, referrer->descriptor, referrer->classLoader);
/*
* Class hasn't been loaded yet, or is in the process of being loaded
* and initialized now. Try to get a copy. If we find one, put the
* pointer in the DexTypeId. There isn't a race condition here --
* 32-bit writes are guaranteed atomic on all target platforms. Worst
* case we have two threads storing the same value.
*
* If this is an array class, we'll generate it here.
*/
className = dexStringByTypeIdx(pDvmDex->pDexFile, classIdx);
if (className[0] != '\0' && className[1] == '\0') {
/* primitive type */
resClass = dvmFindPrimitiveClass(className[0]);
} else {
resClass = dvmFindClassNoInit(className, referrer->classLoader);
}
if (resClass != NULL) {
/*
* If the referrer was pre-verified, the resolved class must come
* from the same DEX or from a bootstrap class. The pre-verifier
* makes assumptions that could be invalidated by a wacky class
* loader. (See the notes at the top of oo/Class.c.)
*
* The verifier does *not* fail a class for using a const-class
* or instance-of instruction referring to an unresolveable class,
* because the result of the instruction is simply a Class object
* or boolean -- there's no need to resolve the class object during
* verification. Instance field and virtual method accesses can
* break dangerously if we get the wrong class, but const-class and
* instance-of are only interesting at execution time. So, if we
* we got here as part of executing one of the "unverified class"
* instructions, we skip the additional check.
*
* Ditto for class references from annotations and exception
* handler lists.
*/
if (!fromUnverifiedConstant &&
IS_CLASS_FLAG_SET(referrer, CLASS_ISPREVERIFIED))
{
ClassObject* resClassCheck = resClass;
if (dvmIsArrayClass(resClassCheck))
resClassCheck = resClassCheck->elementClass;
if (referrer->pDvmDex != resClassCheck->pDvmDex &&
resClassCheck->classLoader != NULL)
{
ALOGW("Class resolved by unexpected DEX:"
" %s(%p):%p ref [%s] %s(%p):%p",
referrer->descriptor, referrer->classLoader,
referrer->pDvmDex,
resClass->descriptor, resClassCheck->descriptor,
resClassCheck->classLoader, resClassCheck->pDvmDex);
ALOGW("(%s had used a different %s during pre-verification)",
referrer->descriptor, resClass->descriptor);
dvmThrowIllegalAccessError(
"Class ref in pre-verified class resolved to unexpected "
"implementation");
return NULL;
}
}
LOGVV("##### +ResolveClass(%s): referrer=%s dex=%p ldr=%p ref=%d",
resClass->descriptor, referrer->descriptor, referrer->pDvmDex,
referrer->classLoader, classIdx);
/*
* Add what we found to the list so we can skip the class search
* next time through.
*
* TODO: should we be doing this when fromUnverifiedConstant==true?
* (see comments at top of oo/Class.c)
*/
dvmDexSetResolvedClass(pDvmDex, classIdx, resClass);
} else {
/* not found, exception should be raised */
LOGVV("Class not found: %s",
dexStringByTypeIdx(pDvmDex->pDexFile, classIdx));
assert(dvmCheckException(dvmThreadSelf()));
}
return resClass;
}
/*
* Find the method corresponding to "methodRef".
*
* We use "referrer" to find the DexFile with the constant pool that
* "methodRef" is an index into. We also use its class loader. The method
* being resolved may very well be in a different DEX file.
*
* If this is a static method, we ensure that the method's class is
* initialized.
*/
Method* dvmResolveMethod(const ClassObject* referrer, u4 methodIdx,
MethodType methodType)
{
DvmDex* pDvmDex = referrer->pDvmDex;
ClassObject* resClass;
const DexMethodId* pMethodId;
Method* resMethod;
assert(methodType != METHOD_INTERFACE);
LOGVV("--- resolving method %u (referrer=%s)", methodIdx,
referrer->descriptor);
pMethodId = dexGetMethodId(pDvmDex->pDexFile, methodIdx);
resClass = dvmResolveClass(referrer, pMethodId->classIdx, false);
if (resClass == NULL) {
/* can't find the class that the method is a part of */
assert(dvmCheckException(dvmThreadSelf()));
return NULL;
}
if (dvmIsInterfaceClass(resClass)) {
/* method is part of an interface */
dvmThrowIncompatibleClassChangeErrorWithClassMessage(
resClass->descriptor);
return NULL;
}
const char* name = dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx);
DexProto proto;
dexProtoSetFromMethodId(&proto, pDvmDex->pDexFile, pMethodId);
/*
* We need to chase up the class hierarchy to find methods defined
* in super-classes. (We only want to check the current class
* if we're looking for a constructor; since DIRECT calls are only
* for constructors and private methods, we don't want to walk up.)
*/
if (methodType == METHOD_DIRECT) {
resMethod = dvmFindDirectMethod(resClass, name, &proto);
} else if (methodType == METHOD_STATIC) {
resMethod = dvmFindDirectMethodHier(resClass, name, &proto);
} else {
resMethod = dvmFindVirtualMethodHier(resClass, name, &proto);
}
if (resMethod == NULL) {
std::string msg;
msg += resClass->descriptor;
msg += ".";
msg += name;
dvmThrowNoSuchMethodError(msg.c_str());
return NULL;
}
LOGVV("--- found method %d (%s.%s)",
methodIdx, resClass->descriptor, resMethod->name);
/* see if this is a pure-abstract method */
if (dvmIsAbstractMethod(resMethod) && !dvmIsAbstractClass(resClass)) {
dvmThrowAbstractMethodError(name);
return NULL;
}
/*
* If we're the first to resolve this class, we need to initialize
* it now. Only necessary for METHOD_STATIC.
*/
if (methodType == METHOD_STATIC) {
if (!dvmIsClassInitialized(resMethod->clazz) &&
!dvmInitClass(resMethod->clazz))
{
assert(dvmCheckException(dvmThreadSelf()));
return NULL;
} else {
assert(!dvmCheckException(dvmThreadSelf()));
}
} else {
/*
* Edge case: if the <clinit> for a class creates an instance
* of itself, we will call <init> on a class that is still being
* initialized by us.
*/
assert(dvmIsClassInitialized(resMethod->clazz) ||
dvmIsClassInitializing(resMethod->clazz));
}
/*
* If the class has been initialized, add a pointer to our data structure
* so we don't have to jump through the hoops again. If this is a
* static method and the defining class is still initializing (i.e. this
* thread is executing <clinit>), don't do the store, otherwise other
* threads could call the method without waiting for class init to finish.
*/
if (methodType == METHOD_STATIC && !dvmIsClassInitialized(resMethod->clazz))
{
LOGVV("--- not caching resolved method %s.%s (class init=%d/%d)",
resMethod->clazz->descriptor, resMethod->name,
dvmIsClassInitializing(resMethod->clazz),
dvmIsClassInitialized(resMethod->clazz));
} else {
dvmDexSetResolvedMethod(pDvmDex, methodIdx, resMethod);
}
return resMethod;
}
/*
* Resolve an interface method reference.
*
* Returns NULL with an exception raised on failure.
*/
Method* dvmResolveInterfaceMethod(const ClassObject* referrer, u4 methodIdx)
{
DvmDex* pDvmDex = referrer->pDvmDex;
ClassObject* resClass;
const DexMethodId* pMethodId;
Method* resMethod;
LOGVV("--- resolving interface method %d (referrer=%s)",
methodIdx, referrer->descriptor);
pMethodId = dexGetMethodId(pDvmDex->pDexFile, methodIdx);
resClass = dvmResolveClass(referrer, pMethodId->classIdx, false);
if (resClass == NULL) {
/* can't find the class that the method is a part of */
assert(dvmCheckException(dvmThreadSelf()));
return NULL;
}
if (!dvmIsInterfaceClass(resClass)) {
/* whoops */
dvmThrowIncompatibleClassChangeErrorWithClassMessage(
resClass->descriptor);
return NULL;
}
/*
* This is the first time the method has been resolved. Set it in our
* resolved-method structure. It always resolves to the same thing,
* so looking it up and storing it doesn't create a race condition.
*
* If we scan into the interface's superclass -- which is always
* java/lang/Object -- we will catch things like:
* interface I ...
* I myobj = (something that implements I)
* myobj.hashCode()
* However, the Method->methodIndex will be an offset into clazz->vtable,
* rather than an offset into clazz->iftable. The invoke-interface
* code can test to see if the method returned is abstract or concrete,
* and use methodIndex accordingly. I'm not doing this yet because
* (a) we waste time in an unusual case, and (b) we're probably going
* to fix it in the DEX optimizer.
*
* We do need to scan the superinterfaces, in case we're invoking a
* superinterface method on an interface reference. The class in the
* DexTypeId is for the static type of the object, not the class in
* which the method is first defined. We have the full, flattened
* list in "iftable".
*/
const char* methodName =
dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx);
DexProto proto;
dexProtoSetFromMethodId(&proto, pDvmDex->pDexFile, pMethodId);
LOGVV("+++ looking for '%s' in resClass='%s'", methodName, resClass->descriptor);
resMethod = dvmFindInterfaceMethodHier(resClass, methodName, &proto);
if (resMethod == NULL) {
std::string msg;
msg += resClass->descriptor;
msg += ".";
msg += methodName;
dvmThrowNoSuchMethodError(msg.c_str());
return NULL;
}
LOGVV("--- found interface method %d (%s.%s)",
methodIdx, resClass->descriptor, resMethod->name);
/* we're expecting this to be abstract */
assert(dvmIsAbstractMethod(resMethod));
/* interface methods are always public; no need to check access */
/*
* The interface class *may* be initialized. According to VM spec
* v2 2.17.4, the interfaces a class refers to "need not" be initialized
* when the class is initialized.
*
* It isn't necessary for an interface class to be initialized before
* we resolve methods on that interface.
*
* We choose not to do the initialization now.
*/
//assert(dvmIsClassInitialized(resMethod->clazz));
/*
* Add a pointer to our data structure so we don't have to jump
* through the hoops again.
*
* As noted above, no need to worry about whether the interface that
* defines the method has been or is currently executing <clinit>.
*/
dvmDexSetResolvedMethod(pDvmDex, methodIdx, resMethod);
return resMethod;
}
/*
* Resolve an instance field reference.
*
* Returns NULL and throws an exception on error (no such field, illegal
* access).
*/
InstField* dvmResolveInstField(const ClassObject* referrer, u4 ifieldIdx)
{
DvmDex* pDvmDex = referrer->pDvmDex;
ClassObject* resClass;
const DexFieldId* pFieldId;
InstField* resField;
LOGVV("--- resolving field %u (referrer=%s cl=%p)",
ifieldIdx, referrer->descriptor, referrer->classLoader);
pFieldId = dexGetFieldId(pDvmDex->pDexFile, ifieldIdx);
/*
* Find the field's class.
*/
resClass = dvmResolveClass(referrer, pFieldId->classIdx, false);
if (resClass == NULL) {
assert(dvmCheckException(dvmThreadSelf()));
return NULL;
}
resField = dvmFindInstanceFieldHier(resClass,
dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx),
dexStringByTypeIdx(pDvmDex->pDexFile, pFieldId->typeIdx));
if (resField == NULL) {
dvmThrowNoSuchFieldError(
dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx));
return NULL;
}
/*
* Class must be initialized by now (unless verifier is buggy). We
* could still be in the process of initializing it if the field
* access is from a static initializer.
*/
assert(dvmIsClassInitialized(resField->clazz) ||
dvmIsClassInitializing(resField->clazz));
/*
* The class is initialized (or initializing), the field has been
* found. Add a pointer to our data structure so we don't have to
* jump through the hoops again.
*
* Anything that uses the resolved table entry must have an instance
* of the class, so any class init activity has already happened (or
* been deliberately bypassed when <clinit> created an instance).
* So it's always okay to update the table.
*/
dvmDexSetResolvedField(pDvmDex, ifieldIdx, (Field*)resField);
LOGVV(" field %u is %s.%s",
ifieldIdx, resField->clazz->descriptor, resField->name);
return resField;
}
/*
* Resolve a static field reference. The DexFile format doesn't distinguish
* between static and instance field references, so the "resolved" pointer
* in the Dex struct will have the wrong type. We trivially cast it here.
*
* Causes the field's class to be initialized.
*/
StaticField* dvmResolveStaticField(const ClassObject* referrer, u4 sfieldIdx)
{
DvmDex* pDvmDex = referrer->pDvmDex;
ClassObject* resClass;
const DexFieldId* pFieldId;
StaticField* resField;
pFieldId = dexGetFieldId(pDvmDex->pDexFile, sfieldIdx);
/*
* Find the field's class.
*/
resClass = dvmResolveClass(referrer, pFieldId->classIdx, false);
if (resClass == NULL) {
assert(dvmCheckException(dvmThreadSelf()));
return NULL;
}
resField = dvmFindStaticFieldHier(resClass,
dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx),
dexStringByTypeIdx(pDvmDex->pDexFile, pFieldId->typeIdx));
if (resField == NULL) {
dvmThrowNoSuchFieldError(
dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx));
return NULL;
}
/*
* If we're the first to resolve the field in which this class resides,
* we need to do it now. Note that, if the field was inherited from
* a superclass, it is not necessarily the same as "resClass".
*/
if (!dvmIsClassInitialized(resField->clazz) &&
!dvmInitClass(resField->clazz))
{
assert(dvmCheckException(dvmThreadSelf()));
return NULL;
}
/*
* If the class has been initialized, add a pointer to our data structure
* so we don't have to jump through the hoops again. If it's still
* initializing (i.e. this thread is executing <clinit>), don't do
* the store, otherwise other threads could use the field without waiting
* for class init to finish.
*/
if (dvmIsClassInitialized(resField->clazz)) {
dvmDexSetResolvedField(pDvmDex, sfieldIdx, (Field*) resField);
} else {
LOGVV("--- not caching resolved field %s.%s (class init=%d/%d)",
resField->clazz->descriptor, resField->name,
dvmIsClassInitializing(resField->clazz),
dvmIsClassInitialized(resField->clazz));
}
return resField;
}
/*
* Resolve a string reference.
*
* Finding the string is easy. We need to return a reference to a
* java/lang/String object, not a bunch of characters, which means the
* first time we get here we need to create an interned string.
*/
StringObject* dvmResolveString(const ClassObject* referrer, u4 stringIdx)
{
DvmDex* pDvmDex = referrer->pDvmDex;
StringObject* strObj;
StringObject* internStrObj;
const char* utf8;
u4 utf16Size;
LOGVV("+++ resolving string, referrer is %s", referrer->descriptor);
/*
* Create a UTF-16 version so we can trivially compare it to what's
* already interned.
*/
utf8 = dexStringAndSizeById(pDvmDex->pDexFile, stringIdx, &utf16Size);
strObj = dvmCreateStringFromCstrAndLength(utf8, utf16Size);
if (strObj == NULL) {
/* ran out of space in GC heap? */
assert(dvmCheckException(dvmThreadSelf()));
goto bail;
}
/*
* Add it to the intern list. The return value is the one in the
* intern list, which (due to race conditions) may or may not be
* the one we just created. The intern list is synchronized, so
* there will be only one "live" version.
*
* By requesting an immortal interned string, we guarantee that
* the returned object will never be collected by the GC.
*
* A NULL return here indicates some sort of hashing failure.
*/
internStrObj = dvmLookupImmortalInternedString(strObj);
dvmReleaseTrackedAlloc((Object*) strObj, NULL);
strObj = internStrObj;
if (strObj == NULL) {
assert(dvmCheckException(dvmThreadSelf()));
goto bail;
}
/* save a reference so we can go straight to the object next time */
dvmDexSetResolvedString(pDvmDex, stringIdx, strObj);
bail:
return strObj;
}
/*
* For debugging: return a string representing the methodType.
*/
const char* dvmMethodTypeStr(MethodType methodType)
{
switch (methodType) {
case METHOD_DIRECT: return "direct";
case METHOD_STATIC: return "static";
case METHOD_VIRTUAL: return "virtual";
case METHOD_INTERFACE: return "interface";
case METHOD_UNKNOWN: return "UNKNOWN";
}
assert(false);
return "BOGUS";
}
|