static std::unique_ptr<const art::DexFile> MakeSingleDexFile(art::Thread* self, constchar* descriptor, const std::string& orig_location,
jint final_len, constunsignedchar* final_dex_data)
REQUIRES_SHARED(art::Locks::mutator_lock_) { // Make the mmap
std::string error_msg;
art::ArrayRef<constunsignedchar> final_data(final_dex_data, final_len);
art::MemMap map = Redefiner::MoveDataToMemMap(orig_location, final_data, &error_msg); if (!map.IsValid()) {
LOG(WARNING) << "Unable to allocate mmap for redefined dex file! Error was: " << error_msg;
self->ThrowOutOfMemoryError(StringPrintf( "Unable to allocate dex file for transformation of %s", descriptor).c_str()); return nullptr;
}
// Make a dex-file if (map.Size() < sizeof(art::DexFile::Header)) {
LOG(WARNING) << "Could not read dex file header because dex_data was too short";
art::ThrowClassFormatError(nullptr, "Unable to read transformed dex file of %s",
descriptor); return nullptr;
}
uint32_t checksum = reinterpret_cast<const art::DexFile::Header*>(map.Begin())->checksum_;
std::string map_name = map.GetName();
art::ArtDexFileLoader dex_file_loader(std::move(map), map_name);
std::unique_ptr<const art::DexFile> dex_file(dex_file_loader.Open(checksum, /*verify=*/true, /*verify_checksum=*/true,
&error_msg)); if (dex_file.get() == nullptr) {
LOG(WARNING) << "Unable to load modified dex file for " << descriptor << ": " << error_msg;
art::ThrowClassFormatError(nullptr, "Unable to read transformed dex file of %s because %s",
descriptor,
error_msg.c_str()); return nullptr;
} if (dex_file->NumClassDefs() != 1) {
LOG(WARNING) << "Dex file contains more than 1 class_def. Ignoring."; // TODO Throw some other sort of error here maybe?
art::ThrowClassFormatError(
nullptr, "Unable to use transformed dex file of %s because it contained too many classes",
descriptor); return nullptr;
} return dex_file;
}
// A deleter that acts like the jvmtiEnv->Deallocate so that asan does not get tripped up. // TODO We should everything use the actual jvmtiEnv->Allocate/Deallocate functions once we can // figure out which env to use. template <typename T> class FakeJvmtiDeleter { public:
FakeJvmtiDeleter() {}
struct ClassCallback : public art::ClassLoadCallback { void ClassPreDefine(constchar* descriptor,
art::Handle<art::mirror::Class> klass,
art::Handle<art::mirror::ClassLoader> class_loader, const art::DexFile& initial_dex_file,
[[maybe_unused]] const art::dex::ClassDef& initial_class_def, /*out*/ art::DexFile const** final_dex_file, /*out*/ art::dex::ClassDef const** final_class_def) override
REQUIRES_SHARED(art::Locks::mutator_lock_) { bool is_enabled =
event_handler->IsEventEnabledAnywhere(ArtJvmtiEvent::kClassFileLoadHookRetransformable) ||
event_handler->IsEventEnabledAnywhere(ArtJvmtiEvent::kClassFileLoadHookNonRetransformable); if (!is_enabled) { return;
} if (descriptor[0] != 'L') { // It is a primitive or array. Just return return;
}
jvmtiPhase phase = PhaseUtil::GetPhaseUnchecked(); if (UNLIKELY(phase != JVMTI_PHASE_START && phase != JVMTI_PHASE_LIVE)) { // We want to wait until we are at least in the START phase so that all WellKnownClasses and // mirror classes have been initialized and loaded. The runtime relies on these classes having // specific fields and methods present. Since PreDefine hooks don't need to abide by this // restriction we will simply not send the event for these classes.
LOG(WARNING) << "Ignoring load of class <" << descriptor << "> as it is being loaded during "
<< "runtime initialization."; return;
}
// Call all non-retransformable agents.
Transformer::CallClassFileLoadHooksSingleClass<
ArtJvmtiEvent::kClassFileLoadHookNonRetransformable>(event_handler, self, &def);
std::vector<unsignedchar> post_non_retransform; if (def.IsModified()) { // Copy the dex data after the non-retransformable events.
post_non_retransform.resize(def.GetDexData().size());
memcpy(post_non_retransform.data(), def.GetDexData().data(), post_non_retransform.size());
}
// Call all structural transformation agents.
Transformer::CallClassFileLoadHooksSingleClass<ArtJvmtiEvent::kStructuralDexFileLoadHook>(
event_handler, self, &def); // Call all retransformable agents.
Transformer::CallClassFileLoadHooksSingleClass<
ArtJvmtiEvent::kClassFileLoadHookRetransformable>(event_handler, self, &def);
if (def.IsModified()) {
VLOG(class_linker) << "Changing class " << descriptor;
art::StackHandleScope<2> hs(self); // Save the results of all the non-retransformable agents. // First allocate the ClassExt
art::Handle<art::mirror::ClassExt> ext =
hs.NewHandle(art::mirror::Class::EnsureExtDataPresent(klass, self)); // Make sure we have a ClassExt. This is fine even though we are a temporary since it will // get copied. if (ext.IsNull()) { // We will just return failure if we fail to allocate
LOG(WARNING) << "Could not allocate ext-data for class '" << descriptor << "'. "
<< "Aborting transformation since we will be unable to store it.";
self->AssertPendingOOMException(); return;
}
// Allocate the byte array to store the dex file bytes in.
art::MutableHandle<art::mirror::Object> arr(hs.NewHandle<art::mirror::Object>(nullptr)); if (post_non_retransform.empty() && strcmp(descriptor, "Ljava/lang/Long;") != 0) { // we didn't have any non-retransformable agents. We can just cache a pointer to the // initial_dex_file. It will be kept live by the class_loader.
jlong dex_ptr = reinterpret_cast<uintptr_t>(&initial_dex_file);
art::JValue val;
val.SetJ(dex_ptr);
arr.Assign(art::BoxPrimitive(art::Primitive::kPrimLong, val));
} else {
arr.Assign(art::mirror::ByteArray::AllocateAndFill(
self, reinterpret_cast<constsignedchar*>(post_non_retransform.data()),
post_non_retransform.size()));
} if (arr.IsNull()) {
LOG(WARNING) << "Unable to allocate memory for initial dex-file. Aborting transformation";
self->AssertPendingOOMException(); return;
}
// TODO Check Redefined dex file for all invariants.
LOG(WARNING) << "Dex file created by class-definition time transformation of "
<< descriptor << " is not checked for all retransformation invariants.";
if (!ClassLoaderHelper::AddToClassLoader(self, class_loader, dex_file.get())) {
LOG(ERROR) << "Unable to add " << descriptor << " to class loader!"; return;
}
// Actually set the ClassExt's original bytes once we have actually succeeded.
ext->SetOriginalDexFile(arr.Get()); // Set the return values
*final_class_def = &dex_file->GetClassDef(0);
*final_dex_file = dex_file.release();
}
}
// To support parallel class-loading, we need to perform some locking dances here. Namely, // the fixup stage must not be holding the temp_classes lock when it fixes up the system // (as that requires suspending all mutators).
for (auto it = temp_classes.begin(); it != temp_classes.end(); ++it) { if (temp_klass.Get() == art::ObjPtr<art::mirror::Class>::DownCast(self->DecodeJObject(*it))) {
self->GetJniEnv()->DeleteGlobalRef(*it);
temp_classes.erase(it);
requires_fixup = true; break;
}
}
} if (requires_fixup) {
FixupTempClass(self, temp_klass, klass);
}
}
void FixupTempClass(art::Thread* self,
art::Handle<art::mirror::Class> temp_klass,
art::Handle<art::mirror::Class> klass)
REQUIRES_SHARED(art::Locks::mutator_lock_) { // Suspend everything.
art::gc::Heap* heap = art::Runtime::Current()->GetHeap(); if (heap->IsGcConcurrentAndMoving()) { // Need to take a heap dump while GC isn't running. See the // comment in Heap::VisitObjects().
heap->IncrementDisableMovingGC(self);
}
{
art::ScopedThreadSuspension sts(self, art::ThreadState::kWaitingForVisitObjects);
art::ScopedSuspendAll ssa("FixupTempClass");
// Fix up the local table with a root visitor.
RootUpdater local_update(local->input_, local->output_);
t->GetJniEnv()->VisitJniLocalRoots(
&local_update, art::RootInfo(art::kRootJNILocal, t->GetThreadId()));
}
voidoperator()(art::mirror::Object* src,
art::MemberOffset field_offset,
[[maybe_unused]] bool is_static) const
REQUIRES_SHARED(art::Locks::mutator_lock_) { if (src->GetFieldObject<art::mirror::Object>(field_offset) == input_) {
DCHECK_NE(field_offset.Uint32Value(), 0u); // This shouldn't be the class field of // an object. // Conservatively use volatile store.
src->SetFieldObjectVolatile</*kTransactionActive=*/ false>(field_offset, output_);
}
}
voidoperator()([[maybe_unused]] art::ObjPtr<art::mirror::Class> klass,
art::ObjPtr<art::mirror::Reference> reference) const
REQUIRES_SHARED(art::Locks::mutator_lock_) {
art::mirror::Object* val = reference->GetReferent(); if (val == input_) {
reference->SetReferent<false>(output_);
}
}
// A set of all the temp classes we have handed out. We have to fix up references to these. // For simplicity, we store the temp classes as JNI global references in a vector. Normally a // Prepare event will closely follow, so the vector should be small.
std::mutex temp_classes_lock;
std::vector<jclass> temp_classes;
// Check if this class is a temporary class object used for loading. Since we are seeing it the // class must not have been prepared yet since otherwise the fixup would have gotten the jobject // to point to the final class object. if (klass->IsTemp() || klass->IsRetired()) { return ERR(CLASS_NOT_PREPARED);
}
// Check if this class is a temporary class object used for loading. Since we are seeing it the // class must not have been prepared yet since otherwise the fixup would have gotten the jobject // to point to the final class object. if (klass->IsTemp() || klass->IsRetired()) { return ERR(CLASS_NOT_PREPARED);
}
// Need to handle array specifically. Arrays implement Serializable and Cloneable, but the // spec says these should not be reported. if (klass->IsArrayClass()) {
*interface_count_ptr = 0;
*interfaces_ptr = nullptr; // TODO: Should we allocate a placeholder here? return ERR(NONE);
}
if (status_ptr == nullptr) { return ERR(NULL_POINTER);
}
if (klass->IsArrayClass()) {
*status_ptr = JVMTI_CLASS_STATUS_ARRAY;
} elseif (klass->IsPrimitive()) {
*status_ptr = JVMTI_CLASS_STATUS_PRIMITIVE;
} else {
*status_ptr = JVMTI_CLASS_STATUS_VERIFIED; // All loaded classes are structurally verified. // This is finicky. If there's an error, we'll say it wasn't prepared. if (klass->IsResolved()) {
*status_ptr |= JVMTI_CLASS_STATUS_PREPARED;
} if (klass->IsInitialized()) {
*status_ptr |= JVMTI_CLASS_STATUS_INITIALIZED;
} // Technically the class may be erroneous for other reasons, but we do not have enough info. if (klass->IsErroneous()) {
*status_ptr |= JVMTI_CLASS_STATUS_ERROR;
}
}
return ERR(NONE);
}
template <typename T> static jvmtiError ClassIsT(jclass jklass, T test, jboolean* is_t_ptr) {
art::ScopedObjectAccess soa(art::Thread::Current());
art::ObjPtr<art::mirror::Class> klass = soa.Decode<art::mirror::Class>(jklass); if (klass == nullptr) { return ERR(INVALID_CLASS);
}
if (is_t_ptr == nullptr) { return ERR(NULL_POINTER);
}
// Copies unique class descriptors into the classes list from dex_files. static jvmtiError CopyClassDescriptors(jvmtiEnv* env, const std::vector<const art::DexFile*>& dex_files, /*out*/jint* count_ptr, /*out*/char*** classes) {
jvmtiError res = OK;
std::set<std::string_view> unique_descriptors;
std::vector<constchar*> descriptors; auto add_descriptor = [&](constchar* desc) { // Don't add duplicates. if (res == OK && unique_descriptors.find(desc) == unique_descriptors.end()) { // The desc will remain valid since we hold a ref to the class_loader.
unique_descriptors.insert(desc);
descriptors.push_back(CopyString(env, desc, &res).release());
}
}; for (const art::DexFile* dex_file : dex_files) {
uint32_t num_defs = dex_file->NumClassDefs(); for (uint32_t i = 0; i < num_defs; i++) {
add_descriptor(dex_file->GetClassDescriptor(dex_file->GetClassDef(i)));
}
} char** out_data = nullptr; if (res == OK) {
res = env->Allocate(sizeof(char*) * descriptors.size(), reinterpret_cast<unsignedchar**>(&out_data));
} if (res != OK) {
env->Deallocate(reinterpret_cast<unsignedchar*>(out_data)); // Failed to allocate. Cleanup everything. for (constchar* data : descriptors) {
env->Deallocate(reinterpret_cast<unsignedchar*>(const_cast<char*>(data)));
}
descriptors.clear(); return res;
} // Everything is good.
memcpy(out_data, descriptors.data(), sizeof(char*) * descriptors.size());
*count_ptr = static_cast<jint>(descriptors.size());
*classes = out_data; return OK;
}
jvmtiError ClassUtil::GetClassLoaderClassDescriptors(jvmtiEnv* env,
jobject loader, /*out*/jint* count_ptr, /*out*/char*** classes) {
art::Thread* self = art::Thread::Current(); if (env == nullptr) { return ERR(INVALID_ENVIRONMENT);
} elseif (self == nullptr) { return ERR(UNATTACHED_THREAD);
} elseif (count_ptr == nullptr || classes == nullptr) { return ERR(NULL_POINTER);
}
std::vector<const art::DexFile*> dex_files_storage; const std::vector<const art::DexFile*>* dex_files = nullptr; if (loader == nullptr) { // We can just get the dex files directly for the boot class path.
dex_files = &art::Runtime::Current()->GetClassLinker()->GetBootClassPath();
} else {
art::ScopedObjectAccess soa(self);
art::StackHandleScope<1> hs(self);
art::Handle<art::mirror::ClassLoader> class_loader(
hs.NewHandle(soa.Decode<art::mirror::ClassLoader>(loader))); if (class_loader->InstanceOf(art::WellKnownClasses::java_lang_BootClassLoader.Get())) { // We can just get the dex files directly for the boot class path.
dex_files = &art::Runtime::Current()->GetClassLinker()->GetBootClassPath();
} elseif (!class_loader->InstanceOf(art::WellKnownClasses::java_lang_ClassLoader.Get())) { return ERR(ILLEGAL_ARGUMENT);
} elseif (!class_loader->InstanceOf(
art::WellKnownClasses::dalvik_system_BaseDexClassLoader.Get())) {
JVMTI_LOG(ERROR, env) << "GetClassLoaderClassDescriptors is only implemented for "
<< "BootClassPath and dalvik.system.BaseDexClassLoader class loaders"; // TODO Possibly return OK With no classes would be better since these ones cannot have any // real classes associated with them. return ERR(NOT_IMPLEMENTED);
} else {
art::VisitClassLoaderDexFiles(
self,
class_loader,
[&](const art::DexFile* dex_file) {
dex_files_storage.push_back(dex_file); returntrue; // Continue with other dex files.
});
dex_files = &dex_files_storage;
}
} // We hold the loader so the dex files won't go away until after this call at worst.
DCHECK(dex_files != nullptr); return CopyClassDescriptors(env, *dex_files, count_ptr, classes);
}
// Note: proxies will show the dex file version of java.lang.reflect.Proxy, as that is // what their dex cache copies from.
uint32_t version = klass->GetDexFile().GetHeader().GetVersion();
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.