// TODO: Optimize. On 32bit we can use an int array.
jboolean is_long_data_copied;
jlong* long_data = env->GetLongArrayElements(reinterpret_cast<jlongArray>(array),
&is_long_data_copied); if (env->ExceptionCheck() == JNI_TRUE) { returnfalse;
}
oat_file = reinterpret_cast64<const OatFile*>(long_data[kOatFileIndex]);
dex_files.reserve(array_size - 1); for (jsize i = kDexFileIndexStart; i < array_size; ++i) {
dex_files.push_back(reinterpret_cast64<const DexFile*>(long_data[i]));
}
// Now release all the unique_ptrs. for (auto& dex_file : vec) {
dex_file.release(); // NOLINT
}
return long_array;
}
// A smart pointer that provides read-only access to a Java string's UTF chars. // Unlike libcore's NullableScopedUtfChars, this will *not* throw NullPointerException if // passed a null jstring. The correct idiom is: // // NullableScopedUtfChars name(env, javaName); // if (env->ExceptionCheck()) { // return null; // } // // ... use name.c_str() // // TODO: rewrite to get rid of this, or change ScopedUtfChars to offer this option. class NullableScopedUtfChars { public:
NullableScopedUtfChars(JNIEnv* env, jstring s) : mEnv(env), mString(s) {
mUtfChars = (s != nullptr) ? env->GetStringUTFChars(s, nullptr) : nullptr;
}
~NullableScopedUtfChars() { if (mUtfChars) {
mEnv->ReleaseStringUTFChars(mString, mUtfChars);
}
}
// Disallow copy and assignment.
NullableScopedUtfChars(const NullableScopedUtfChars&); voidoperator=(const NullableScopedUtfChars&);
};
static jobject CreateCookieFromOatFileManagerResult(
JNIEnv* env,
std::vector<std::unique_ptr<const DexFile>>& dex_files, const OatFile* oat_file, const std::vector<std::string>& error_msgs) {
ClassLinker* linker = Runtime::Current()->GetClassLinker(); if (dex_files.empty()) {
ScopedObjectAccess soa(env);
CHECK(!error_msgs.empty()); // The most important message is at the end. So set up nesting by going forward, which will // wrap the existing exception as a cause for the following one. auto it = error_msgs.begin(); auto itEnd = error_msgs.end(); for ( ; it != itEnd; ++it) {
ThrowWrappedIOException("%s", it->c_str());
} return nullptr;
}
if (array == nullptr) { // Direct ByteBuffer
uint8_t* base_address = reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(buffer)); if (base_address == nullptr) {
ScopedObjectAccess soa(env);
ThrowWrappedIOException("dexFileBuffer not direct"); return nullptr;
}
size_t length = static_cast<size_t>(end - start);
memcpy(dex_data.Begin(), base_address + start, length);
} else { // ByteBuffer backed by a byte array
jbyte* destination = reinterpret_cast<jbyte*>(dex_data.Begin());
env->GetByteArrayRegion(array, start, end - start, destination);
}
dex_mem_maps.push_back(std::move(dex_data));
}
// Hand MemMaps over to OatFileManager to open the dex files and potentially // create a backing OatFile instance from an anonymous vdex.
std::vector<std::string> error_msgs; const OatFile* oat_file = nullptr;
std::vector<std::unique_ptr<const DexFile>> dex_files =
Runtime::Current()->GetOatFileManager().OpenDexFilesFromOat(std::move(dex_mem_maps),
class_loader,
dex_elements, /*out*/ &oat_file, /*out*/ &error_msgs); return CreateCookieFromOatFileManagerResult(env, dex_files, oat_file, error_msgs);
}
#ifdef ART_TARGET_ANDROID staticbool isReadOnlyJavaDclEnforced(JNIEnv* env) { staticbool is_at_least_u = [] { constint api_level = android_get_device_api_level(); if (api_level > __ANDROID_API_T__) { returntrue;
} elseif (api_level == __ANDROID_API_T__) { // Check if running U preview char value[92] = {0}; if (__system_property_get("ro.build.version.preview_sdk", value) >= 0 && atoi(value) > 0) { returntrue;
}
} returnfalse;
}(); if (is_at_least_u) { // The reason why we are calling the AppCompat framework through JVM // instead of directly using the CompatFramework C++ API is because feature // overrides only apply to the Java API. // CtsLibcoreTestCases is part of mainline modules, which requires the same test // to run on older Android versions; the target SDK of CtsLibcoreTestCases is locked // to the lowest supported API level (at the time of writing, it's API 31). // We would need to be able to manually enable the compat change in CTS tests.
ScopedLocalRef<jclass> compat(env, env->FindClass("android/compat/Compatibility"));
jmethodID mId = env->GetStaticMethodID(compat.get(), "isChangeEnabled", "(J)Z"); return env->CallStaticBooleanMethod(compat.get(), mId, kEnforceReadOnlyJavaDcl) == JNI_TRUE;
} else { returnfalse;
}
} #else// ART_TARGET_ANDROID
constexpr staticbool isReadOnlyJavaDclEnforced(JNIEnv*) {
(void)kEnforceReadOnlyJavaDcl; returnfalse;
} #endif// ART_TARGET_ANDROID
staticbool isReadOnlyJavaDclChecked() { if (!kIsTargetAndroid) { returnfalse;
} constint uid = getuid(); // The following UIDs are exempted: // * Root (0): root processes always have write access to files. // * System (1000): /data/app/**.apk are owned by AID_SYSTEM; // loading installed APKs in system_server is allowed. // * Shell (2000): directly calling dalvikvm/app_process in ADB shell // to run JARs with CLI is allowed. return uid != 0 && uid != 1000 && uid != 2000;
}
// TODO(calin): clean up the unused parameters (here and in libcore). static jobject DexFile_openDexFileNative(JNIEnv* env,
jclass,
jstring javaSourceName,
[[maybe_unused]] jstring javaOutputName,
[[maybe_unused]] jint flags,
jobject class_loader,
jobjectArray dex_elements) {
ScopedUtfChars sourceName(env, javaSourceName); if (sourceName.c_str() == nullptr) { return nullptr;
}
if (isReadOnlyJavaDclChecked() && access(sourceName.c_str(), W_OK) == 0) {
LOG(ERROR) << "Attempt to load writable dex file: " << sourceName.c_str(); if (isReadOnlyJavaDclEnforced(env)) {
ScopedLocalRef<jclass> se(env, env->FindClass("java/lang/SecurityException"));
std::string message(
StringPrintf("Writable dex file '%s' is not allowed.", sourceName.c_str()));
env->ThrowNew(se.get(), message.c_str()); return nullptr;
}
}
// Extract list of dex files from the cookie.
std::vector<const DexFile*> dex_files; const OatFile* oat_file; if (!ConvertJavaArrayToDexFiles(env, cookie, dex_files, oat_file)) {
Thread::Current()->AssertPendingException(); return;
}
CHECK(oat_file == nullptr) << "Called verifyInBackground on a dex file backed by oat";
// Hand over to OatFileManager to spawn a verification thread.
Runtime::Current()->GetOatFileManager().RunBackgroundVerification(
dex_files,
class_loader);
}
static jboolean DexFile_closeDexFile(JNIEnv* env, jclass, jobject cookie) {
std::vector<const DexFile*> dex_files; const OatFile* oat_file; if (!ConvertJavaArrayToDexFiles(env, cookie, dex_files, oat_file)) {
Thread::Current()->AssertPendingException(); return JNI_FALSE;
}
Runtime* const runtime = Runtime::Current(); bool all_deleted = true; // We need to clear the caches since they may contain pointers to the dex instructions. // Different dex file can be loaded at the same memory location later by chance.
Thread::ClearAllInterpreterCaches();
{
ScopedObjectAccess soa(env);
ObjPtr<mirror::Object> dex_files_object = soa.Decode<mirror::Object>(cookie);
ObjPtr<mirror::LongArray> long_dex_files = dex_files_object->AsLongArray(); // Delete dex files associated with this dalvik.system.DexFile since there should not be running // code using it. dex_files is a vector due to multidex.
ClassLinker* const class_linker = runtime->GetClassLinker();
int32_t i = kDexFileIndexStart; // Oat file is at index 0. for (const DexFile* dex_file : dex_files) { if (dex_file != nullptr) {
RemoveNativeDebugInfoForDex(soa.Self(), dex_file); // Only delete the dex file if the dex cache is not found to prevent runtime crashes // if there are calls to DexFile.close while the ART DexFile is still in use. if (!class_linker->IsDexFileRegistered(soa.Self(), *dex_file)) { // Clear the element in the array so that we can call close again.
long_dex_files->Set(i, 0);
class_linker->RemoveDexFromCaches(*dex_file); delete dex_file;
} else {
all_deleted = false;
}
}
++i;
}
}
// oat_file can be null if we are running without dex2oat. if (all_deleted && oat_file != nullptr) { // If all of the dex files are no longer in use we can unmap the corresponding oat file.
VLOG(class_linker) << "Unregistering " << oat_file;
runtime->GetOatFileManager().UnRegisterAndDeleteOatFile(oat_file);
} return all_deleted ? JNI_TRUE : JNI_FALSE;
}
ScopedUtfChars class_name(env, javaName); if (class_name.c_str() == nullptr) {
VLOG(class_linker) << "Failed to find class_name"; return nullptr;
} const std::string descriptor = DotToDescriptor(class_name); const size_t hash = ComputeModifiedUtf8Hash(descriptor); for (auto& dex_file : dex_files) { const dex::ClassDef* dex_class_def = OatDexFile::FindClassDef(*dex_file, descriptor, hash); if (dex_class_def != nullptr) {
ScopedObjectAccess soa(env);
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
StackHandleScope<1> hs(soa.Self());
Handle<mirror::ClassLoader> class_loader(
hs.NewHandle(soa.Decode<mirror::ClassLoader>(javaLoader)));
ObjPtr<mirror::DexCache> dex_cache =
class_linker->RegisterDexFile(*dex_file, class_loader.Get()); if (dex_cache == nullptr) { // OOME or InternalError (dexFile already registered with a different class loader).
soa.Self()->AssertPendingException(); return nullptr;
}
ObjPtr<mirror::Class> result = class_linker->DefineClass(soa.Self(),
descriptor.c_str(),
descriptor.length(),
hash,
class_loader,
*dex_file,
*dex_class_def); // Add the used dex file. This only required for the DexFile.loadClass API since normal // class loaders already keep their dex files live.
class_linker->InsertDexFileInToClassLoader(soa.Decode<mirror::Object>(dexFile),
class_loader.Get()); if (result != nullptr) {
VLOG(class_linker) << "DexFile_defineClassNative returning " << result
<< " for " << class_name.c_str(); return soa.AddLocalReference<jclass>(result);
}
}
}
VLOG(class_linker) << "Failed to find dex_class_def " << class_name.c_str(); return nullptr;
}
// Needed as a compare functor for sets of const char struct CharPointerComparator { booloperator()(constchar *str1, constchar *str2) const { return strcmp(str1, str2) < 0;
}
};
// Note: this can be an expensive call, as we sort out duplicates in MultiDex files. static jobjectArray DexFile_getClassNameList(JNIEnv* env, jclass, jobject cookie) { const OatFile* oat_file = nullptr;
std::vector<const DexFile*> dex_files; if (!ConvertJavaArrayToDexFiles(env, cookie, /*out */ dex_files, /* out */ oat_file)) {
DCHECK(env->ExceptionCheck()); return nullptr;
}
// Push all class descriptors into a set. Use set instead of unordered_set as we want to // retrieve all in the end.
std::set<constchar*, CharPointerComparator> descriptors; for (auto& dex_file : dex_files) { for (size_t i = 0; i < dex_file->NumClassDefs(); ++i) { const dex::ClassDef& class_def = dex_file->GetClassDef(i); constchar* descriptor = dex_file->GetClassDescriptor(class_def);
descriptors.insert(descriptor);
}
}
// Now create output array and copy the set into it.
ScopedObjectAccess soa(Thread::ForEnv(env)); auto descriptor_to_dot = [](constchar* descriptor) { return DescriptorToDot(descriptor); }; return soa.AddLocalReference<jobjectArray>(CreateStringArray(
soa.Self(),
descriptors.size(),
MakeTransformRange(descriptors, descriptor_to_dot)));
}
// Return an array specifying the optimization status of the given file. // The array specification is [compiler_filter, compiler_reason]. static jobjectArray DexFile_getDexFileOptimizationStatus(JNIEnv* env,
jclass,
jstring javaFilename,
jstring javaInstructionSet) {
ScopedUtfChars filename(env, javaFilename); if (env->ExceptionCheck()) { return nullptr;
}
ScopedUtfChars instruction_set(env, javaInstructionSet); if (env->ExceptionCheck()) { return nullptr;
}
const InstructionSet target_instruction_set = GetInstructionSetFromString(
instruction_set.c_str()); if (target_instruction_set == InstructionSet::kNone) {
ScopedLocalRef<jclass> iae(env, env->FindClass("java/lang/IllegalArgumentException"));
std::string message(StringPrintf("Instruction set %s is invalid.", instruction_set.c_str()));
env->ThrowNew(iae.get(), message.c_str()); return nullptr;
}
// Filter stayed the same, return input. if (filter == new_filter) { return javaCompilerFilter;
}
// Create a new string object and return.
std::string new_filter_str = CompilerFilter::NameOfFilter(new_filter); return env->NewStringUTF(new_filter_str.c_str());
}
// Filter stayed the same, return input. if (filter == new_filter) { return javaCompilerFilter;
}
// Create a new string object and return.
std::string new_filter_str = CompilerFilter::NameOfFilter(new_filter); return env->NewStringUTF(new_filter_str.c_str());
}
ScopedUtfChars instruction_set(env, javaInstructionSet); if (env->ExceptionCheck()) { return nullptr;
}
const InstructionSet target_instruction_set = GetInstructionSetFromString(
instruction_set.c_str()); if (target_instruction_set == InstructionSet::kNone) {
ScopedLocalRef<jclass> iae(env, env->FindClass("java/lang/IllegalArgumentException"));
std::string message(StringPrintf("Instruction set %s is invalid.", instruction_set.c_str()));
env->ThrowNew(iae.get(), message.c_str()); return nullptr;
}
std::string oat_filename;
std::string vdex_filename; // Check if the file is in the boot classpath by looking at image spaces which // have oat files. bool is_vdex_only = false; for (gc::space::ImageSpace* space : Runtime::Current()->GetHeap()->GetBootImageSpaces()) { const OatFile* oat_file = space->GetOatFile(); if (oat_file != nullptr) { const std::vector<const OatDexFile*>& oat_dex_files = oat_file->GetOatDexFiles(); for (const OatDexFile* oat_dex_file : oat_dex_files) { if (DexFileLoader::GetBaseLocation(oat_dex_file->GetDexFileLocation()) ==
filename.c_str()) {
oat_filename = oat_file->GetLocation();
is_vdex_only = oat_file->IsBackedByVdexOnly(); break;
}
} if (!oat_filename.empty()) { break;
}
}
}
// If we did not find a boot classpath oat file, lookup the oat file for an app. if (oat_filename.empty()) {
OatFileAssistant oat_file_assistant(filename.c_str(),
target_instruction_set, /* context= */ nullptr, /* load_executable= */ false);
// Currently only allow this for debuggable apps. if (!runtime->IsJavaDebuggableAtInit()) {
ThrowSecurityException("Can't exempt class, process is not debuggable."); return;
}
// Assign core platform domain as the dex files are allowed to access all the other domains. for (const DexFile* dex_file : dex_files) { const_cast<DexFile*>(dex_file)->SetHiddenapiDomain(hiddenapi::Domain::kCorePlatform);
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.