Do I need to save classes of the same package in the same descriptor when using multiple dex files

About the title

"classes of the same package" means classes that have the same access to packages. Note that the abcFoo class does not have access to the abBar class. Since the latter cannot access the former, if the former modifier is by default.

Problem

If I split two classes in the same packages into two dex files, although I load them correctly, I also get some error while working, the trap is like:

I / dalvikvm (6498): DexOpt: illegal access to the method (calling Lcom / fish47 / multidex / Foo; .isWholeWord (Lcom / fish47 / multidex / Foo;) Z from Lcom / fish47 / multidex / TestMatchWord;)
I / dalvikvm (6498): Could not find com.fish47.multidex.core.Foo.isWholeWord method referenced by com.fish47.multidex.core.TestMatchWord.test_english
W / dalvikvm (6498): VFY: cannot enable virtual method 758: Lcom / fish47 / multidex / Foo; .isWholeWord (Lcom / fish47 / multidex / Foo;) Z


Hypothesis

This code throws an error message below:
vm / analysis / Optimize.c ==> line: 697 - 714

/* access allowed? */ tweakLoader(referrer, resMethod->clazz); bool allowed = dvmCheckMethodAccess(referrer, resMethod); untweakLoader(referrer, resMethod->clazz); if (!allowed) { IF_LOGI() { char* desc = dexProtoCopyMethodDescriptor(&resMethod->prototype); LOGI("DexOpt: illegal method access (call %s.%s %s from %s)\n", resMethod->clazz->descriptor, resMethod->name, desc, referrer->descriptor); free(desc); } if (pFailure != NULL) *pFailure = VERIFY_ERROR_ACCESS_METHOD; return NULL; } 


Note the value of resClass-> classLoader . If two classes are not from the same dex file, its value will be set to 0xdead3333 .
vm / analysis / Optimize.c ==> line: 285 - 299

 static void tweakLoader(ClassObject* referrer, ClassObject* resClass) { if (!gDvm.optimizing) return; assert(referrer->classLoader == NULL); assert(resClass->classLoader == NULL); if (!gDvm.optimizingBootstrapClass) { /* class loader for an array class comes from element type */ if (dvmIsArrayClass(resClass)) resClass = resClass->elementClass; if (referrer->pDvmDex != resClass->pDvmDex) resClass->classLoader = (Object*) 0xdead3333; } } 


This is a trick, it allows the checkAccess (...) method to return false finally, if two classes are in the same package, accessible to each other, but not public.
vm / oo / AccessCheck.c ==> line: 88 - 116

 static bool checkAccess(const ClassObject* accessFrom, const ClassObject* accessTo, u4 accessFlags) { /* quick accept for public access */ if (accessFlags & ACC_PUBLIC) return true; /* quick accept for access from same class */ if (accessFrom == accessTo) return true; /* quick reject for private access from another class */ if (accessFlags & ACC_PRIVATE) return false; /* * Semi-quick test for protected access from a sub-class, which may or * may not be in the same package. */ if (accessFlags & ACC_PROTECTED) if (dvmIsSubClass(accessFrom, accessTo)) return true; /* * Allow protected and private access from other classes in the same * package. */ return dvmInSamePackage(accessFrom, accessTo); } 

vm / oo / AccessCheck.c ==> line: 39 - 83

 bool dvmInSamePackage(const ClassObject* class1, const ClassObject* class2) { ... /* class loaders must match */ if (class1->classLoader != class2->classLoader) return false; ... } 
+4
source share
1 answer

This is a somewhat strange area, mainly due to the “pre-validation” and optimization performed by dexopt. For the background, you should read the comments at the beginning of oo / Class.cpp (lines 39-153).

(Note: the files were changed from “.c” to “.cpp” in ICS. You should probably look at current sources, although in practice there has been little change in the last few years.)

Generally speaking, two classes in the same package in different DEX files could access each other with the package area if both DEX files are loaded by the same class loader. This is what was tested in AccessCheck.cpp.

What you are looking for in Optimize.cpp is a parallel converter implementation - dvmOptResolveClass vs. dvmResolveClass - which is used during validation and optimization. It will configure the class loader, as you noted, but only if it works inside dexopt (which means checking with !gDvm.optimizing ). If it is inside a normally executable instance of a virtual machine, the bootloader will not be changed during the checks.

When launched as part of dexopt, the code in Optimize.cpp either checks + optimizes the boot classes, or checks + optimizes a single DEX file without loading. In any case, all DEX files are loaded through the bootloader because the virtual machine is not working, and this is the only way to load classes. (The dexopt point should check as many classes as possible during assembly or installation, so we don’t need to do this when the application starts. More about dexopt here .)

The code in tweakLoader says: if I am in dexopt and I am not optimizing the actual DEX file for initial loading (e.g. framework.jar), then I need to make sure that the package scope checks assume that the classes in the current DEX file are not loaded by the class loader bootstrap.

For example, I could create a class called java.lang.Stuff in my application. In dexopt, since everything is loaded with a single loader, it will be able to touch the package-private material in other java.lang classes if we do not select the loader. When the application actually runs, the java.lang classes are taken from the boot loader, and the Stuff class comes from the application loader, so these calls should be prohibited.

So what the code does. As for your specific problem, I expect the calls to work as long as the same bootloader is used to load both DEX files. If one DEX is loaded with application infrastructure and the other with custom DexClassLoader , then I did not expect it to work.

One more note: the errors you inserted mention both com.fish47.multidex.Foo and com.fish47.multidex.core.Foo , which are not the same package. I do not know if this is related. In addition, if there are additional VFY messages, it is useful to include them, even if they are a little incomprehensible. And for whatever it is, it’s also important to indicate which version of Android you are using - it has not changed after a while, but if you come back far enough, it’s completely different.

+4
source

Source: https://habr.com/ru/post/1497426/


All Articles