1 /**
2 * Copyright (c) 2021-2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "runtime/class_initializer.h"
17
18 #include "include/object_header.h"
19 #include "libpandafile/file_items.h"
20 #include "macros.h"
21 #include "runtime/include/class_linker.h"
22 #include "runtime/include/coretypes/tagged_value.h"
23 #include "runtime/include/runtime.h"
24 #include "runtime/handle_scope-inl.h"
25 #include "runtime/monitor.h"
26 #include "runtime/monitor_object_lock.h"
27 #include "runtime/global_object_lock.h"
28 #include "runtime/coroutines/coroutine.h"
29 #include "runtime/coroutines/coroutine_manager.h"
30 #include "verification/util/is_system.h"
31 #include "verify_app_install.h"
32
33 namespace ark {
34 template <MTModeT MODE>
35 class ObjectLockConfig {
36 };
37
38 template <>
39 class ObjectLockConfig<MT_MODE_MULTI> {
40 public:
41 using LockT = ObjectLock;
42 };
43
44 /// The "TASK" multithreading mode implies that M coroutines are running on N worker threads
45 template <>
46 class ObjectLockConfig<MT_MODE_TASK> {
47 public:
48 /**
49 * NOTE(konstanting):
50 * For the sake of simplicity we use a global mutex-like lock for synchronization. We imply that there will be no
51 * coroutine switch during the class initialization and that assumption includes static constructor bodies too.
52 * With a global lock, coroutine switch during a class initialiation sequence will possibly lead to deadlocks and
53 * other failures, so expect various checkers to fire and warn you in case a coroutine switch is detected.
54 *
55 * In future, we probably we would like to have coroutine-friendly per-class locks as it is a more performant
56 * solution, which places less restrictions on the class initialization sequence.
57 */
58 using LockT = GlobalObjectLock;
59 };
60
61 template <>
62 class ObjectLockConfig<MT_MODE_SINGLE> {
63 public:
64 class DummyObjectLock {
65 public:
DummyObjectLock(ObjectHeader * header)66 explicit DummyObjectLock(ObjectHeader *header [[maybe_unused]]) {}
67 ~DummyObjectLock() = default;
Wait(bool ignoreInterruption=false)68 bool Wait([[maybe_unused]] bool ignoreInterruption = false)
69 {
70 return true;
71 }
TimedWait(uint64_t timeout)72 bool TimedWait([[maybe_unused]] uint64_t timeout)
73 {
74 return true;
75 }
Notify()76 void Notify() {}
NotifyAll()77 void NotifyAll() {}
78 NO_COPY_SEMANTIC(DummyObjectLock);
79 NO_MOVE_SEMANTIC(DummyObjectLock);
80 };
81
82 using LockT = DummyObjectLock;
83 };
84
85 /// Does nothing in MT_MODE_SINGLE and MT_MODE_MULTI
86 template <MTModeT MODE>
87 class ClassInitGuard {
88 public:
89 using Guard = struct Dummy {
90 explicit Dummy(ThreadManager *tm)
91 {
92 // GCC 8 and below has a strange bug: it reports a false syntax error in case
93 // when [[maybe_unused]] is set for the first constructor argument.
94 // NOTE(konstanting): revert to [[maybe_unused]] when we do not use GCC<=8
95 UNUSED_VAR(tm);
96 }
97 };
98 };
99
100 /// Disables coroutine switch in MT_MODE_TASK (required by the current synchronization scheme)
101 template <>
102 class ClassInitGuard<MT_MODE_TASK> {
103 public:
104 using Guard = class Adapter {
105 public:
106 explicit Adapter(ThreadManager *tm) : s_(static_cast<CoroutineManager *>(tm)) {}
107
108 private:
109 ScopedDisableCoroutineSwitch s_;
110 };
111 };
112
WrapException(ClassLinker * classLinker,ManagedThread * thread)113 static void WrapException(ClassLinker *classLinker, ManagedThread *thread)
114 {
115 LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(*thread->GetException()->ClassAddr<Class>());
116 ctx.WrapClassInitializerException(classLinker, thread);
117 }
118
ThrowNoClassDefFoundError(ManagedThread * thread,const Class * klass)119 static void ThrowNoClassDefFoundError(ManagedThread *thread, const Class *klass)
120 {
121 LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(*klass);
122 auto name = klass->GetName();
123 ThrowException(ctx, thread, ctx.GetNoClassDefFoundErrorDescriptor(), utf::CStringAsMutf8(name.c_str()));
124 }
125
ThrowEarlierInitializationException(ManagedThread * thread,const Class * klass)126 static void ThrowEarlierInitializationException(ManagedThread *thread, const Class *klass)
127 {
128 ASSERT(klass->IsErroneous());
129
130 ThrowNoClassDefFoundError(thread, klass);
131 }
132
ThrowIncompatibleClassChangeError(ManagedThread * thread,const Class * klass)133 static void ThrowIncompatibleClassChangeError(ManagedThread *thread, const Class *klass)
134 {
135 LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(*klass);
136 auto name = klass->GetName();
137 ThrowException(ctx, thread, ctx.GetIncompatibleClassChangeErrorDescriptor(), utf::CStringAsMutf8(name.c_str()));
138 }
139
ThrowVerifyError(ManagedThread * thread,const Class * klass)140 static void ThrowVerifyError(ManagedThread *thread, const Class *klass)
141 {
142 LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(*klass);
143 auto name = klass->GetName();
144 ThrowException(ctx, thread, ctx.GetVerifyErrorClassDescriptor(), utf::CStringAsMutf8(name.c_str()));
145 }
146
IsBadSuperClass(const Class * base,ManagedThread * thread,const Class * klass)147 static bool IsBadSuperClass(const Class *base, ManagedThread *thread, const Class *klass)
148 {
149 if (base->IsInterface()) {
150 ThrowIncompatibleClassChangeError(thread, klass);
151 return true;
152 }
153
154 if (base->IsFinal()) {
155 ThrowVerifyError(thread, klass);
156 return true;
157 }
158
159 return false;
160 }
161
162 template <class ObjectLockT>
WaitInitialization(ObjectLockT * lock,ClassLinker * classLinker,ManagedThread * thread,Class * klass)163 static bool WaitInitialization(ObjectLockT *lock, ClassLinker *classLinker, ManagedThread *thread, Class *klass)
164 {
165 while (true) {
166 // Should be possible to interrupt Wait for termination
167 auto state = lock->Wait(false);
168 if (!state && thread->IsRuntimeTerminated()) {
169 // If daemon threads are terminated then wait may be interrupted,
170 // otherwise continue waiting
171 ThrowNoClassDefFoundError(thread, klass);
172 klass->SetState(Class::State::ERRONEOUS);
173 return false;
174 }
175
176 if (thread->HasPendingException()) {
177 WrapException(classLinker, thread);
178 klass->SetState(Class::State::ERRONEOUS);
179 return false;
180 }
181
182 if (klass->IsInitializing()) {
183 continue;
184 }
185
186 if (klass->IsErroneous()) {
187 ThrowNoClassDefFoundError(thread, klass);
188 return false;
189 }
190
191 if (klass->IsInitialized()) {
192 return true;
193 }
194
195 UNREACHABLE();
196 }
197 }
198
199 /* static */
200 template <MTModeT MODE>
Initialize(ClassLinker * classLinker,ManagedThread * thread,Class * klass)201 bool ClassInitializer<MODE>::Initialize(ClassLinker *classLinker, ManagedThread *thread, Class *klass)
202 {
203 if (klass->IsInitialized()) {
204 return true;
205 }
206
207 // embraces the class init sequence with some checkers in the MT_MODEs where it is required
208 [[maybe_unused]] typename ClassInitGuard<MODE>::Guard guard(thread->GetVM()->GetThreadManager());
209
210 using ObjectLockT = typename ObjectLockConfig<MODE>::LockT;
211
212 [[maybe_unused]] HandleScope<ObjectHeader *> scope(thread);
213 VMHandle<ObjectHeader> managedClassObjHandle(thread, klass->GetManagedObject());
214 {
215 ObjectLockT lock(managedClassObjHandle.GetPtr());
216
217 if (klass->IsInitialized()) {
218 return true;
219 }
220
221 if (klass->IsErroneous()) {
222 ThrowEarlierInitializationException(thread, klass);
223 return false;
224 }
225
226 if (!InitClassVerificationMode(klass)) {
227 return false;
228 }
229
230 if (klass->IsInitializing()) {
231 if constexpr (MODE == MT_MODE_TASK) {
232 if (klass->GetInitTid() == Coroutine::CastFromThread(thread)->GetCoroutineId()) {
233 return true;
234 }
235 } else {
236 if (klass->GetInitTid() == thread->GetId()) {
237 return true;
238 }
239 }
240
241 if constexpr ((MODE == MT_MODE_MULTI) || (MODE == MT_MODE_TASK)) {
242 return WaitInitialization(&lock, classLinker, thread, klass);
243 }
244
245 UNREACHABLE();
246 }
247
248 if constexpr (MODE == MT_MODE_TASK) {
249 klass->SetInitTid(Coroutine::CastFromThread(thread)->GetCoroutineId());
250 } else {
251 klass->SetInitTid(thread->GetId());
252 }
253 klass->SetState(Class::State::INITIALIZING);
254 if (!ClassInitializer::InitializeFields(klass)) {
255 LOG(ERROR, CLASS_LINKER) << "Cannot initialize fields of class '" << klass->GetName() << "'";
256 return false;
257 }
258 }
259
260 LOG(DEBUG, CLASS_LINKER) << "Initializing class " << klass->GetName();
261
262 return InitializeClass(classLinker, thread, klass, managedClassObjHandle);
263 }
264
265 template <MTModeT MODE>
InitClassVerificationMode(Class * klass)266 bool ClassInitializer<MODE>::InitClassVerificationMode(Class *klass)
267 {
268 const auto &options = Runtime::GetCurrent()->GetOptions();
269 switch (options.GetVerificationMode()) {
270 case VerificationMode::DISABLED:
271 if (!klass->IsVerified()) {
272 klass->SetState(Class::State::VERIFIED);
273 }
274 return true;
275 case VerificationMode::AHEAD_OF_TIME:
276 if (!klass->IsVerified()) {
277 if (!VerifyClass(klass)) {
278 klass->SetState(Class::State::ERRONEOUS);
279 ark::ThrowVerificationException(utf::Mutf8AsCString(klass->GetDescriptor()));
280 return false;
281 }
282 }
283 return true;
284 case VerificationMode::ON_THE_FLY:
285 if (options.IsArkAot()) {
286 LOG(FATAL, VERIFIER) << "On the fly verification mode is not compatible with ark_aot";
287 }
288 return true;
289 default:
290 UNREACHABLE();
291 }
292 }
293
294 /* static */
295 template <MTModeT MODE>
InitializeClass(ClassLinker * classLinker,ManagedThread * thread,Class * klass,const VMHandle<ObjectHeader> & managedClassObjHandle)296 bool ClassInitializer<MODE>::InitializeClass(ClassLinker *classLinker, ManagedThread *thread, Class *klass,
297 const VMHandle<ObjectHeader> &managedClassObjHandle)
298 {
299 using ObjectLockT = typename ObjectLockConfig<MODE>::LockT;
300
301 if (!klass->IsInterface()) {
302 auto *base = klass->GetBase();
303
304 if (base != nullptr) {
305 if (IsBadSuperClass(base, thread, klass)) {
306 return false;
307 }
308
309 if (!Initialize(classLinker, thread, base)) {
310 ObjectLockT lock(managedClassObjHandle.GetPtr());
311 klass->SetState(Class::State::ERRONEOUS);
312 lock.NotifyAll();
313 return false;
314 }
315 }
316
317 for (auto *iface : klass->GetInterfaces()) {
318 if (iface->IsInitialized()) {
319 continue;
320 }
321
322 if (!InitializeInterface(classLinker, thread, iface, klass)) {
323 ObjectLockT lock(managedClassObjHandle.GetPtr());
324 klass->SetState(Class::State::ERRONEOUS);
325 lock.NotifyAll();
326 return false;
327 }
328 }
329 }
330
331 LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(*klass);
332 Method::Proto proto(Method::Proto::ShortyVector {panda_file::Type(panda_file::Type::TypeId::VOID)},
333 Method::Proto::RefTypeVector {});
334 auto *cctorName = ctx.GetCctorName();
335 auto *cctor = klass->GetDirectMethod(cctorName, proto);
336 if (cctor != nullptr) {
337 cctor->InvokeVoid(thread, nullptr);
338 }
339
340 {
341 ObjectLockT lock(managedClassObjHandle.GetPtr());
342
343 if (thread->HasPendingException()) {
344 WrapException(classLinker, thread);
345 klass->SetState(Class::State::ERRONEOUS);
346 lock.NotifyAll();
347 return false;
348 }
349
350 klass->SetState(Class::State::INITIALIZED);
351
352 lock.NotifyAll();
353 }
354
355 return true;
356 }
357
358 /* static */
359 template <MTModeT MODE>
InitializeInterface(ClassLinker * classLinker,ManagedThread * thread,Class * iface,Class * klass)360 bool ClassInitializer<MODE>::InitializeInterface(ClassLinker *classLinker, ManagedThread *thread, Class *iface,
361 Class *klass)
362 {
363 if (!iface->IsInterface()) {
364 ThrowIncompatibleClassChangeError(thread, klass);
365 return false;
366 }
367
368 for (auto *baseIface : iface->GetInterfaces()) {
369 if (baseIface->IsInitialized()) {
370 continue;
371 }
372
373 if (!InitializeInterface(classLinker, thread, baseIface, klass)) {
374 return false;
375 }
376 }
377
378 if (!iface->HasDefaultMethods()) {
379 return true;
380 }
381
382 return Initialize(classLinker, thread, iface);
383 }
384
385 /* static */
386 template <MTModeT MODE>
VerifyClass(Class * klass)387 bool ClassInitializer<MODE>::VerifyClass(Class *klass)
388 {
389 ASSERT(!klass->IsVerified());
390
391 auto &opts = Runtime::GetCurrent()->GetOptions();
392
393 if (!IsVerifySuccInAppInstall(klass->GetPandaFile())) {
394 LOG(ERROR, CLASS_LINKER) << "verify fail";
395 return false;
396 }
397
398 bool skipVerification = !opts.IsVerifyRuntimeLibraries() && verifier::IsSystemOrSyntheticClass(klass);
399 if (skipVerification) {
400 for (auto &method : klass->GetMethods()) {
401 method.SetVerified(true);
402 }
403 klass->SetState(Class::State::VERIFIED);
404 return true;
405 }
406
407 LOG(INFO, VERIFIER) << "Verification of class '" << klass->GetName() << "'";
408
409 for (auto &method : klass->GetMethods()) {
410 if (!method.Verify()) {
411 return false;
412 }
413 }
414
415 klass->SetState(Class::State::VERIFIED);
416 return true;
417 }
418
419 template <class T>
InitializePrimitiveField(Class * klass,const Field & field)420 static void InitializePrimitiveField(Class *klass, const Field &field)
421 {
422 panda_file::FieldDataAccessor fda(*field.GetPandaFile(), field.GetFileId());
423 auto value = fda.GetValue<T>();
424 klass->SetFieldPrimitive<T>(field, value ? value.value() : 0);
425 }
426
InitializeTaggedField(Class * klass,const Field & field)427 static void InitializeTaggedField(Class *klass, const Field &field)
428 {
429 LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(*klass);
430 klass->SetFieldPrimitive<coretypes::TaggedValue>(field, ctx.GetInitialTaggedValue());
431 }
432
InitializeStringField(Class * klass,const Field & field)433 static void InitializeStringField(Class *klass, const Field &field)
434 {
435 panda_file::FieldDataAccessor fda(*field.GetPandaFile(), field.GetFileId());
436 auto value = fda.GetValue<uint32_t>();
437 if (value) {
438 panda_file::File::EntityId id(value.value());
439 coretypes::String *str = Runtime::GetCurrent()->GetPandaVM()->ResolveString(*klass->GetPandaFile(), id);
440 if (LIKELY(str != nullptr)) {
441 klass->SetFieldObject(field, str);
442 return;
443 }
444 }
445 // Should nullptr be set?
446 klass->SetFieldObject(field, nullptr);
447 }
448
449 /* static */
450 template <MTModeT MODE>
InitializeFields(Class * klass)451 bool ClassInitializer<MODE>::InitializeFields(Class *klass)
452 {
453 using Type = panda_file::Type;
454
455 for (const auto &field : klass->GetStaticFields()) {
456 switch (field.GetTypeId()) {
457 case Type::TypeId::U1:
458 case Type::TypeId::U8:
459 InitializePrimitiveField<uint8_t>(klass, field);
460 break;
461 case Type::TypeId::I8:
462 InitializePrimitiveField<int8_t>(klass, field);
463 break;
464 case Type::TypeId::I16:
465 InitializePrimitiveField<int16_t>(klass, field);
466 break;
467 case Type::TypeId::U16:
468 InitializePrimitiveField<uint16_t>(klass, field);
469 break;
470 case Type::TypeId::I32:
471 InitializePrimitiveField<int32_t>(klass, field);
472 break;
473 case Type::TypeId::U32:
474 InitializePrimitiveField<uint32_t>(klass, field);
475 break;
476 case Type::TypeId::I64:
477 InitializePrimitiveField<int64_t>(klass, field);
478 break;
479 case Type::TypeId::U64:
480 InitializePrimitiveField<uint64_t>(klass, field);
481 break;
482 case Type::TypeId::F32:
483 InitializePrimitiveField<float>(klass, field);
484 break;
485 case Type::TypeId::F64:
486 InitializePrimitiveField<double>(klass, field);
487 break;
488 case Type::TypeId::TAGGED:
489 InitializeTaggedField(klass, field);
490 break;
491 case Type::TypeId::REFERENCE:
492 InitializeStringField(klass, field);
493 break;
494 default: {
495 UNREACHABLE();
496 break;
497 }
498 }
499 }
500
501 return true;
502 }
503
504 template class ClassInitializer<MT_MODE_SINGLE>;
505 template class ClassInitializer<MT_MODE_MULTI>;
506 template class ClassInitializer<MT_MODE_TASK>;
507 } // namespace ark
508