IOS App startup optimization (I) : Detect startup time
IOS App startup Optimization (2) : Physical memory and virtual memory
IOS App startup optimization (3) : binary rearrangement
IOS App startup optimization (4) : pile && get method symbol at compile time
IOS App startup optimization (5) : Collect symbols && generate Order File
IOS App launch optimization (vi) : Utility party directly see here
Relevant concepts
Compiler staking is modifying existing code or generating new code during code compilation.
At compile time, add hook code to the binary source data inside each function to achieve the global hook effect.
Compile-time staking will cover LLVM, but it has little impact on the actual implementation of the code, so you can look at the concepts
LLVM and compilation process
Compile the LLVM
How to find a way to insert a pile
We need to track the execution of each method to obtain the order in which the methods were executed at startup, and then write the order file in that order.
Clang santizer coverage is used for the implementation of tracking.
Don’t panic, open the documentation and take a look at the ~ Clang documentation
Documentation is important. What if there’s a demo in there?
Code Coverage tool
LLVM
SanitizerCoverage
- It can insert user-defined functions and provide callbacks at the function, block, and edge levels
- It enables simple visual coverage reports
Tracing PCs with Guards
The document is a good thing.
Don’t know how to use it? It’s not a problem.
The specific implementation
Add the set
Target -> Build Setting -> Custom Complier Flags -> Other C Flags add -fsanitize- Coverage = trace-PC-guard
Add methods
I’m doing it in viewController, so I’m just copying these two methods in
void __sanitizer_cov_trace_pc_guard_init(uint32_t *start,
uint32_t *stop) {
static uint64_t N; // Counter for the guards.
if (start == stop || *start) return; // Initialize only once.
printf("INIT: %p %p\n", start, stop);
for (uint32_t *x = start; x < stop; x++)
*x = ++N; // Guards should start from 1.
}
void __sanitizer_cov_trace_pc_guard(uint32_t *guard) {
if(! *guard)return;
void *PC = __builtin_return_address(0);
char PcDescr[1024];
printf("guard: %p %x PC %s\n", guard, *guard, PcDescr);
}
Copy the code
Run the code and take a look
__sanitizer_cov_trace_pc_guard_init
What is this… I don’t understand it, but let’s make a break point
Start contains a bunch of serial numbers. Will stop also contain serial numbers?
Check stop to see if it is not the serial number, which is embarrassing…
Thought for a while found that want to know the last serial number, should stop address moved forward to check again!
Let’s move forward four bytes
Start and stop are 01 to 0e, 1 to 14 in decimal.
Could this be the number of the function? Try assigning him a function
0e
0f
Block, C function
See here may feel ~ hook can get this is what big deal. Let’s do something new
- Add a
block
- Add a
C functions
0xf + 2 = 0x11
But that’s not enough. What if I’m a mix? Try adding a swfit function.
Swift mixed processing
Target -> Build Setting -> Custom Complier Flags -> Other Swift Flags added
-sanitize-coverage=func
-sanitize=undefined
Run the code, output the address
The number has increased to 0x1a, so I guess the generator file generates a lot of native methods at the same time, so let’s mask the newly created block, c function, oc function and see if the number becomes 1a-0x3.
1a - 0x3 = 0x17
__sanitizer_cov_trace_pc_guard
The guard_init method above can get the number of all methods, so there must be a way to get the specific information about the method. The key is the __sanitizer_cov_trace_pc_guard, which is analyzed next.
Add the click method and call the method you just added
To run the code, tap the screen twice
Every time you click on the final output test, you can see that each click enters the Guard method eight times. Check it out at the [ViewController touchesBegan:withEvent:] breakpoint.
You can see that the __sanitizer_cov_trace_pc_guard method is inserted when the method is called.
This is actually the first line of code execution inside the method, after which the line of code is stack balancing and preparing register data.
Obtain method address
After executing __sanitizer_COv_trace_pc_GUARD breakpoint, read the contents of lr register
So here you can see that inside the LR is storing [ViewController touchesBegan:withEvent:].
That’s amazing. Did it happen again? No, if you re-execute it you’ll get stuck in an infinite loop, which obviously the code doesn’t.
When functions are nested, the jump function BL saves the address of the next instruction in X30, which is the LR register.
FuncA calls funcB, which in assembly will be translated as bl + 0x???? , which first stores the address of the next assembly instruction in the X30 register and then executes at the specified address passed after the jump to BL.
The principle of bl jump to an address is to change the value of the PC register to point to the address to jump to.
In fact, funcB also protects the values of registers X29 and X30, preventing subfunctions from jumping to other functions overwriting the values of X30.
When funcB executes the return instruction RET, it reads the address of X30 and jumps back, returning to the next step in the function above it.
So in this case, __sanitizer_cov_trace_pc_guard is returned to the first line of the ViewController touchesBegan:withEvent:.
In other words, we can get the address of the original method in __sanitizer_cov_trace_pc_guard! Let’s see how it’s done.
The key is the __builtin_return_address function, which essentially reads the address stored in X30 to return to the current instruction. Then this PC is the method address we want!
Get method symbol
Import header file
#import <dlfcn.h>
Copy the code
Dlfcn.h has a dladdr() method that finds the function symbol by its internal address.
This method requires the structure Dl_info, which contains some additional information ~
Get the Dl_info one by one and print it out.
Sname we want the method symbol, and the order is the order of the call function!
Now that the compiler has staked the method symbol, how about going to the project and writing the order file?
No ~ too young too simple ~ t.t.
Next, please see iOS App startup optimization (5) : Collect symbols && generate Order File