• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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