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
|
/*
* Copyright (C) 2017 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.
*/
#include "aot_class_linker.h"
#include "class_status.h"
#include "compiler_callbacks.h"
#include "dex/class_reference.h"
#include "gc/heap.h"
#include "handle_scope-inl.h"
#include "mirror/class-inl.h"
#include "runtime.h"
#include "verifier/verifier_enums.h"
namespace art {
AotClassLinker::AotClassLinker(InternTable* intern_table)
: ClassLinker(intern_table, /*fast_class_not_found_exceptions=*/ false) {}
AotClassLinker::~AotClassLinker() {}
bool AotClassLinker::CanAllocClass() {
// AllocClass doesn't work under transaction, so we abort.
if (Runtime::Current()->IsActiveTransaction()) {
Runtime::Current()->AbortTransactionAndThrowAbortError(
Thread::Current(), "Can't resolve type within transaction.");
return false;
}
return ClassLinker::CanAllocClass();
}
// Wrap the original InitializeClass with creation of transaction when in strict mode.
bool AotClassLinker::InitializeClass(Thread* self,
Handle<mirror::Class> klass,
bool can_init_statics,
bool can_init_parents) {
Runtime* const runtime = Runtime::Current();
bool strict_mode = runtime->IsActiveStrictTransactionMode();
DCHECK(klass != nullptr);
if (klass->IsInitialized() || klass->IsInitializing()) {
return ClassLinker::InitializeClass(self, klass, can_init_statics, can_init_parents);
}
// When compiling a boot image extension, do not initialize a class defined
// in a dex file belonging to the boot image we're compiling against.
// However, we must allow the initialization of TransactionAbortError,
// VerifyError, etc. outside of a transaction.
if (!strict_mode && runtime->GetHeap()->ObjectIsInBootImageSpace(klass->GetDexCache())) {
if (runtime->IsActiveTransaction()) {
runtime->AbortTransactionAndThrowAbortError(self, "Can't initialize " + klass->PrettyTypeOf()
+ " because it is defined in a boot image dex file.");
return false;
}
CHECK(klass->IsThrowableClass()) << klass->PrettyDescriptor();
}
// When in strict_mode, don't initialize a class if it belongs to boot but not initialized.
if (strict_mode && klass->IsBootStrapClassLoaded()) {
runtime->AbortTransactionAndThrowAbortError(self, "Can't resolve "
+ klass->PrettyTypeOf() + " because it is an uninitialized boot class.");
return false;
}
// Don't initialize klass if it's superclass is not initialized, because superclass might abort
// the transaction and rolled back after klass's change is commited.
if (strict_mode && !klass->IsInterface() && klass->HasSuperClass()) {
if (klass->GetSuperClass()->GetStatus() == ClassStatus::kInitializing) {
runtime->AbortTransactionAndThrowAbortError(self, "Can't resolve "
+ klass->PrettyTypeOf() + " because it's superclass is not initialized.");
return false;
}
}
if (strict_mode) {
runtime->EnterTransactionMode(/*strict=*/ true, klass.Get());
}
bool success = ClassLinker::InitializeClass(self, klass, can_init_statics, can_init_parents);
if (strict_mode) {
if (success) {
// Exit Transaction if success.
runtime->ExitTransactionMode();
} else {
// If not successfully initialized, don't rollback immediately, leave the cleanup to compiler
// driver which needs abort message and exception.
DCHECK(self->IsExceptionPending());
}
}
return success;
}
verifier::FailureKind AotClassLinker::PerformClassVerification(Thread* self,
Handle<mirror::Class> klass,
verifier::HardFailLogMode log_level,
std::string* error_msg) {
Runtime* const runtime = Runtime::Current();
CompilerCallbacks* callbacks = runtime->GetCompilerCallbacks();
ClassStatus old_status = callbacks->GetPreviousClassState(
ClassReference(&klass->GetDexFile(), klass->GetDexClassDefIndex()));
// Was it verified? Report no failure.
if (old_status >= ClassStatus::kVerified) {
return verifier::FailureKind::kNoFailure;
}
if (old_status >= ClassStatus::kVerifiedNeedsAccessChecks) {
return verifier::FailureKind::kAccessChecksFailure;
}
// Does it need to be verified at runtime? Report soft failure.
if (old_status >= ClassStatus::kRetryVerificationAtRuntime) {
// Error messages from here are only reported through -verbose:class. It is not worth it to
// create a message.
return verifier::FailureKind::kSoftFailure;
}
// Do the actual work.
return ClassLinker::PerformClassVerification(self, klass, log_level, error_msg);
}
bool AotClassLinker::CanReferenceInBootImageExtension(ObjPtr<mirror::Class> klass, gc::Heap* heap) {
// Do not allow referencing a class or instance of a class defined in a dex file
// belonging to the boot image we're compiling against but not itself in the boot image;
// or a class referencing such classes as component type, superclass or interface.
// Allowing this could yield duplicate class objects from multiple extensions.
if (heap->ObjectIsInBootImageSpace(klass)) {
return true; // Already included in the boot image we're compiling against.
}
// Treat arrays and primitive types specially because they do not have a DexCache that we
// can use to check whether the dex file belongs to the boot image we're compiling against.
DCHECK(!klass->IsPrimitive()); // Primitive classes must be in the primary boot image.
if (klass->IsArrayClass()) {
DCHECK(heap->ObjectIsInBootImageSpace(klass->GetIfTable())); // IfTable is OK.
// Arrays of all dimensions are tied to the dex file of the non-array component type.
do {
klass = klass->GetComponentType();
} while (klass->IsArrayClass());
if (klass->IsPrimitive()) {
return false;
}
// Do not allow arrays of erroneous classes (the array class is not itself erroneous).
if (klass->IsErroneous()) {
return false;
}
}
// Check the class itself.
if (heap->ObjectIsInBootImageSpace(klass->GetDexCache())) {
return false;
}
// Check superclasses.
ObjPtr<mirror::Class> superclass = klass->GetSuperClass();
while (!heap->ObjectIsInBootImageSpace(superclass)) {
DCHECK(superclass != nullptr); // Cannot skip Object which is in the primary boot image.
if (heap->ObjectIsInBootImageSpace(superclass->GetDexCache())) {
return false;
}
superclass = superclass->GetSuperClass();
}
// Check IfTable. This includes direct and indirect interfaces.
ObjPtr<mirror::IfTable> if_table = klass->GetIfTable();
for (size_t i = 0, num_interfaces = klass->GetIfTableCount(); i < num_interfaces; ++i) {
ObjPtr<mirror::Class> interface = if_table->GetInterface(i);
DCHECK(interface != nullptr);
if (!heap->ObjectIsInBootImageSpace(interface) &&
heap->ObjectIsInBootImageSpace(interface->GetDexCache())) {
return false;
}
}
if (kIsDebugBuild) {
// All virtual methods must come from classes we have already checked above.
PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
ObjPtr<mirror::Class> k = klass;
while (!heap->ObjectIsInBootImageSpace(k)) {
for (auto& m : k->GetVirtualMethods(pointer_size)) {
ObjPtr<mirror::Class> declaring_class = m.GetDeclaringClass();
CHECK(heap->ObjectIsInBootImageSpace(declaring_class) ||
!heap->ObjectIsInBootImageSpace(declaring_class->GetDexCache()));
}
k = k->GetSuperClass();
}
}
return true;
}
bool AotClassLinker::SetUpdatableBootClassPackages(const std::vector<std::string>& packages) {
DCHECK(updatable_boot_class_path_descriptor_prefixes_.empty());
// Transform package names to descriptor prefixes.
std::vector<std::string> prefixes;
prefixes.reserve(packages.size());
for (const std::string& package : packages) {
if (package.empty() || package.find('/') != std::string::npos) {
LOG(ERROR) << "Invalid package name: " << package;
return false;
}
std::string prefix = 'L' + package + '/';
std::replace(prefix.begin(), prefix.end(), '.', '/');
prefixes.push_back(std::move(prefix));
}
// Sort and remove unnecessary prefixes.
std::sort(prefixes.begin(), prefixes.end());
std::string last_prefix;
auto end_it = std::remove_if(
prefixes.begin(),
prefixes.end(),
[&last_prefix](const std::string& s) {
if (!last_prefix.empty() && StartsWith(s, last_prefix)) {
return true;
} else {
last_prefix = s;
return false;
}
});
prefixes.resize(std::distance(prefixes.begin(), end_it));
prefixes.shrink_to_fit();
updatable_boot_class_path_descriptor_prefixes_.swap(prefixes);
return true;
}
bool AotClassLinker::IsUpdatableBootClassPathDescriptor(const char* descriptor) {
std::string_view descriptor_sv(descriptor);
for (const std::string& prefix : updatable_boot_class_path_descriptor_prefixes_) {
if (StartsWith(descriptor_sv, prefix)) {
return true;
}
}
return false;
}
} // namespace art
|