The core idea of acd is to extract ObjC class informations and create everything at a program-controlled stage instead of letting libObjC do the initialization for us.
Dependency Resolving
OOP languages have a core concept called “Classes”, ObjC is no difference.For our transform to work as intended, we need to create classes strictly following the dependency graph.And that means:
- Classes that don’t have a base class should be created first.
- Following classes that has external dependency, which can be distinguished at IR level by checking if the BaseClass is definition or declaration.This is done using
GlobalVariable::hasInitializer ()
- Loop through the remaining classes using a deque.Which we repeat the following process until the deque is empty:
- Pop the front-most class
- Check if this class’s dependency is available
- If available,process the class
- Otherwise,push the class to the back of the queue
The same also applies for protocols. Note we are running our pass at LTO stage to make sure local classes are sorted correctly
Platform-Dependent Data Sizes
Certain ObjC Runtime API uses type like size_t
which size is platform dependent.
Critical DataStructures like class_ro_t
has platform specific definition:
1 | struct class_ro_t { |
These issues are resolved by parsing Module’s Triple.
Handle IvarLayout
Just add class_setIvarLayout
and class_setWeakIvarLayout
calls
Provide Mode Switching
Full LTO is not always possible. As such we should provide three modes:
- Injecting at global initializers
- Replace original metadata_ro_t and leave only
+initialize
,perform method adding there. Ivars and props are unchanged - User Custom Initializing using
__attribute__((annotate("FLAGS")))
, which is converted tollvm.global.annotations
inllvm.metadata
Fix ro_flags
Google RO_IS_ARR
, we need to add instructions to copy flags over after creating class
Misc Structs
CGObjCMac.cpp
‘s comments explained the structs and their meanings in great detail.Below is IR-Level struct and their original definitions
%struct._ivar_t = type { i64*, i8*, i8*, i32, i32 }
1 | struct _ivar_t { |
%struct._ivar_list_t = type { i32, i32, [0 x %struct._ivar_t] }
1 | struct _ivar_list_t { |
1 | %struct._protocol_t = |
1 | struct _protocol_t { |
1 | %struct._objc_protocol_list = type { i64, [0 x %struct._protocol_t*] } |
1 | struct _protocol_list_t { |
1 | %struct._prop_t |
1 | struct _prop_t { |
T.B.C