A lot of premain code, uncontrollable, bombs are always on the line.To make the developer transition more "transparent", there are the following methods.
The source of the idea is still three articles that analyzed Facebook clients two years ago:
1-Explore facebook iOS client-section fbsessiongks
https://everettjf.github.io/2016/08/21/facebook-explore-section-fbsessiongks/
2-Explore facebook iOS Client-section FBInjectable
https://everettjf.github.io/2016/08/20/facebook-explore-section-fbinjectable/
3-Explore Facebook iOS Client-Section RODATA
https://everettjf.github.io/2016/08/19/facebook-explore-section-rodata/
There are three ways to get your code to execute before the main function:
-
All +load methods
-
All C++ static initializers
-
All C/C++ attribute(constructor) functions
Problems Executing Before main Function
-
Cannot Patch
-
Cannot audit time-consuming
-
Calling UIKit-related methods will cause some classes to initialize earlier
-
Main thread execution, fully blocked execution
How to solve these problems
Whether there is a convenient way to port code that precedes the main function after the main function.
Idea Source
Facebook has a new section FBInjectable, so you can learn the meaning of this section by putting some data into a custom section during compilation and linking, and then getting the section's data in your program.
If the data is a string, we can get the class name from the string; if it is a function address, we can call it directly.
(Refer to the article https://everettjf.github.io/2016/08/20/facebook-explore-section-fbinjectable for the meaning of Facebook paragraph FBInjectable)
So how do I create an FBInjectable segment?
You can use the u attribute((used,section("segmentname,sectionname")) keyword to put a variable into a special section.
(attribute s refer to http://gcc.gnu.org/onlinedocs/gcc-3.2/gcc/Variable-Attributes.html)
For example:
char * kString1 __attribute((used,section("__DATA,FBInjectable"))) = "string 1"; char * kString2 __attribute((used,section("__DATA,FBInjectable"))) = "string 2"; char * kString3 __attribute((used,section("__DATA,FBInjectable"))) = "string 3";
After compilation, you can create a new FBInjectable section under the program's DATA segment s and use the addresses of kString1, kString2, and kString3 variables as FBInjectable section content.
How to apply
Simulate Facebook's code, which places the address of the function address (the value of varSampleObject) in the QWLoadable section.
typedef void (*QWLoadableFunctionTemplate)(); static void QWLoadableSampleFunction(){ // Do something } static QWLoadableFunctionTemplate varSampleObject __attribute((used, section("__DATA,QWLoadable"))) = QWLoadableSampleFunction;
Then the main program obtains the contents of QWLoadable through getsectiondata at startup and calls them one by one.
Further Improvement
To mark the name of each function, you can have the function pass out from within as follows:
typedef int (*QWLoadableFunctionCallback)(const char *); typedef void (*QWLoadableFunctionTemplate)(QWLoadableFunctionCallback); static void QWLoadableSampleFunction(QWLoadableFunctionCallback QWLoadableCallback){ if(0 != QWLoadableCallback("SampleObject")) return; // Do something } static QWLoadableFunctionTemplate varSampleObject __attribute((used, section("__DATA,QWLoadable"))) = QWLoadableSampleFunction;
This allows the function to tell the outside about its "identity" through QWLoadableCallback and gives the outside the ability to filter itself (without calling).
Invoked at startup
static int QWLoadableFunctionCallbackImpl(const char *name){ // filter by name return 0; } static void QWLoadableRun(){ CFTimeInterval loadStart = CFAbsoluteTimeGetCurrent(); Dl_info info; int ret = dladdr(QWLoadableRun, &info); if(ret == 0){ // fatal error } #ifndef __LP64__ const struct mach_header *mhp = (struct mach_header*)info.dli_fbase; unsigned long size = 0; uint32_t *memory = (uint32_t*)getsectiondata(mhp, QWLoadableSegmentName, QWLoadableSectionName, & size); #else /* defined(__LP64__) */ const struct mach_header_64 *mhp = (struct mach_header_64*)info.dli_fbase; unsigned long size = 0; uint64_t *memory = (uint64_t*)getsectiondata(mhp, QWLoadableSegmentName, QWLoadableSectionName, & size); #endif /* defined(__LP64__) */ CFTimeInterval loadComplete = CFAbsoluteTimeGetCurrent(); NSLog(@"QWLoadable:loadcost:%@ms",@(1000.0*(loadComplete-loadStart))); if(size == 0){ NSLog(@"QWLoadable:empty"); return; } for(int idx = 0; idx < size/sizeof(void*); ++idx){ QWLoadableFunctionTemplate func = (QWLoadableFunctionTemplate)memory[idx]; func(QWLoadableFunctionCallbackImpl); } NSLog(@"QWLoadable:callcost:%@ms",@(1000.0*(CFAbsoluteTimeGetCurrent()-loadComplete))); }
Reform
Callers can migrate code originally in + load between two macros (QWLoadableFunctionBegin and QWLoadableFunctionEnd) as follows.
QWLoadableFunctionBegin(FooObject) [BarObject userDefinedLoad]; // anything here QWLoadableFunctionEnd(FooObject)
Dynamic Library
Dynamic libraries are individual, so segments of QWLoadable in dynamic libraries need to be handled separately.
performance
Porting code before the main function such as + load to the main function also adds a time-consuming read section.
Tested, 100 function addresses read, not more than 1ms on the device of the iPhone 5.This adds less than 1ms of time (which is also auditable), bringing auditable behavior for all start-up phases, and the most important Patch capabilities.
Reference Code
https://github.com/everettjf/Yolo/tree/master/LoadableMacro
summary
This is just the simplest example. Any address can be stored in a section, which is more flexible.