This article documents the common practice of binary rearrangement

The method of collecting function calls in this paper is Clang piling

Clang piling can be done:

Obj -c’s anonymous function Block

Swift code method

Custom C functions/system C functions

Obj-c method of our/system

Because when Clang compiles our code, generating IR,

There is an AST abstract syntax tree for handling call dependencies

The example in this article is a demo of MJRefresh for the OBJ-C project

The background is briefly

(Lots of information online)

The operating system has a Page fault. A page fault occurs for about 4 ms, and the user does not perceive it

When the app was started, a large number of page faults occurred, which was easily perceived by users

Feel the default compile order of the startup methods

Use the link map

  • Look at the symbols below

This is the default link order, which is independent of the call order

Address Size File Name

0x100005B5C 0x0000008C [ 1] -[MJRefreshBackFooter willMoveToSuperview:]

0x100005BE8 0x00000340 [ 1] -[MJRefreshBackFooter scrollViewContentOffsetDidChange:]

.

  • Access to the link map

Set within the Xcode

In the folder. Find it

The key to binary rearrangement is to sort out the stack of function calls that are started

Answer in the interview, arrange manually, that GG

Because calls branch and they repeat

There is some complexity

Clang piling is used in this paper

In other C Flags, enter

-fsanitize-coverage=func,trace-pc-guard

After setting,

#include

Two C functions fire

void __sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop)

with

void __sanitizer_cov_trace_pc_guard(uint32_t *guard)

Clang inserts the second method into the method call we care about

I get the address of the last call

Set the pile, generally only for performance optimization

Usually development and production, generally close the effect of pile insertion

Next, get the symbol

Import the # import < DLFCN. H >

Through the DLADDR method

Note:
  • 1. Avoid loops

-fsanitize-coverage=func

Clang peg, by default, every time the cycle, also collected

=func, has the effect of inserting pile, ignoring circular jump

  • 2. Thread safety

Atomic queues can be used

Import the # import < libkern/OSAtomic. H >

static OSQueueHead symbolList = OS_ATOMIC_QUEUE_INIT;

    1. Remove duplicate

Remove the collected duplicate symbols

  • 4. Symbol processing

Obj-c class method, prefixed with +[

An instance method of Obj -c, prefixed with -[

C method, manually prefix, _

  • 5. Performance optimization

In piling, do as little as possible

In the piling method, only addresses are collected

After symbol collection is completed, unified parsing processing is performed

  • 6, details

When is the symbol collection finished

Binary rearrangement, do is start optimization

After rendering the first screen,

Click on the screen, you can calculate

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event

If you choose a list, you can do it

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath

See the Github repo below for the detailed method implementation

order.fileSuch a long
+[UIViewController(Example) load]
_main
+[MJNavigationController initialize]
Copy the code
// ...
Copy the code

Take out and write into the real machine sandboxorder.file

This article is obtained by Xcode

  • 1. Click the device

  • 2. Select the application

  • 3. Download app sandbox information

  • 4. Fetch the file written to the sandbox

order.fileThe setting of the

  • General, and engineering XcodeProj level, relatively simple

  • Write to the compile configuration file

Check the result

Time statistics before main

You can use environment variables

DYLD_PRINT_STATISTICS = 1

After the main function, the first screen controller- (void)viewWillAppear:(BOOL)animatedTime statistics of

The main file

CFAbsoluteTime StartTime; int main(int argc, char * argv[]) { StartTime = CFAbsoluteTimeGetCurrent(); @autoreleasepool { return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); }}Copy the code

In the first screen controller

extern CFAbsoluteTime StartTime; - (void)viewWillAppear:(BOOL)animated{ [super viewWillAppear: animated]; CFAbsoluteTime doneTime = CFAbsoluteTimeGetCurrent(); NSLog(@"AppLanuch-- main after using -- \n% fsec \n",(donetime-startTime)); }Copy the code

Preliminary conclusions: There may be a slight improvement

Before the rearrangement

Total pre-main time: 521-08-25 15:49:26.472878+0800 MJRefreshExample[8439:116264] AppLanuch-- test milliseconds (521-08-25 15:49:26.472878+0800 MJRefreshExample[8439:116264 0.551359 secondsCopy the code

After the rearrangement

Total pre-main time: 227.41 milliseconds (100.0) 2021-08-25 15:47:342.945831 +0800 MJRefreshExample[8382:114265] AppLanuch-- milliseconds (100.0) 2021-08-25 15:47:342.945831 +0800 MJRefreshExample[8382:114265] AppLanuch-- 0.406833 secondsCopy the code

Small projects, not obvious

Every run is different

Step: Xcode Product clean

Delete app on real machine

After deleting, run a few other programs, refresh the hardware, and then run the program test

github repo

LLVM document