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 ablock
  • Add aC 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