preface

In the last chapter of startup optimization – concept, some knowledge related to startup optimization was explained. Finally, the goal of startup optimization can be achieved by reducing pageFault. In this paper, binary rearrangement and Clang pile insertion will be used to achieve the goal of optimization.

Compile order of files and methods

  • Search for Link Map in Build Setting and set Write Link Map File to YES:



    • Then compile and find the corresponding project (called WeChatTest) in DerivedData. Then Build -> Intermediates. Noindex -> WeChatTest. Build -> Debug-iphoneos -> WeChatTest. WeChatTest-LinkMap-normal-arm64.txt



    • Wechattst-linkmap-normal-arm64.txt to find the order in which the code is implemented:



      • AddressIs the address of the code, it addsASLRIt’s an address in physical memory
      • SizeIs the size of memory
      • FileYes file number
      • NameIs the method
  • The order of the code file is determined according to the compile order, and the code inside the file is written from top to bottom



    • This can be verified by changing the compile order of the file and the code in the file. First adjust the compile order of the file:



    • Then, inAppDelegateMethod of adding in



    • View after compilation.txtfile



  • According to the printing of the adjusted method, it can be verified that:

      1. The order of files is determined according to the compile order
      1. The order in which the code in a file is compiled is determined by the order in which it is written to compile from top to bottom

Binary rearrangement

  • In objC4-818.2 you can see a libobjc.order file:



    • Methods in the file are symbolic names, this is for the compiler to see, the compiler will followorderIn order to arrange the binary. So let’s verify that
  • Create a New WushuangDemo1 project and add methods to the AppDelegate and ViewController respectively:

    // AppDelegate.m
    void wushuang() {
    }
    @implementation AppDelegate
    + (void)load {
    }
    
    // ViewController.m
    @implementation ViewController
    + (void)load {
      
    }
    Copy the code
  • Then create a WusHuang. order file in the root directory



    • Write some symbols in Wushuang. Order



      • wushuang666andwushuangSayMethod does not exist
  • Then search for Order File in Build Setting and configure wusHuang. Order path:



  • Finally, set the Write Link Map File of Build Setting to YES, then compile on the real machine, and then open the generated.txt File:



    • The original_mainThe third order is now the first order, the order has been changed, and the symbols that are not there are ignored

We’ve got the binary rearrangement, but our goal is to get the symbol for startup, which involves a lot of things, methods nested functions, network requests in functions, blocks, and so on, too many to read. So how do you know which methods are called at startup, and then you need to use the Clang peg, Clang reads all the code, can override all the functions, blocks, etc.

Clang plugging pile

  • Go to santizer Coverage of the clang official documentation and see Tracing PC, which is a pointer to code read by the CPU. Tracing PC is the Tracing PC. Santizercoverage is a simple code coverage tool built into LLVM that inserts callbacks at functions, methods, and blocks, provides default implementations of these callbacks, and implements simple coverage and visualization.



Hook function call

  • In the Tracing PCs with Guards section, add -fsanitize- Coverage = trace-PC-guard configuration in the compiler, and the system will implement two functions by default:

    __sanitizer_cov_trace_pc_guard(&guard_variable)
    __sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop)
    Copy the code

    Let’s verify:

    • Create a new project inBuild SettingIn theOther C Flagsadd-fsanitize-coverage=trace-pc-guard



    • Then, inA:An error occurs when the symbols of these two methods are not found when compiling in the environment



    • So addflagAfter that, the system will call these two functions, which need to be implemented manually. Copy to the implementation case given in the documentationViewControllerIn the



    • At this point the compilation is successful
  • The __sanitizer_cov_trace_pc_guard_init function reflects the number of symbols in the project

    • The parameterstartandstopIs on behalf of4 bytesThe data,forA loop is traversalstarttostopAnd then we’ll talk about initialization4 bytesThe variable increments and is then placed at the appropriate position in the traversal
    • Run it and print itstartandstopAddress, usexCommand readsstartMemory can get the stored data



    • So the last number is going to bestopMemory minus4After getThe first 4 bytesdata



    • Increase methodtouchesBegan:Method, and then run to see the last data, found increased1



    • Let’s add another onewushuangFunction to see if it adds more1



    • It turns out that the last number is still going to+ 1, indicating that the function can also monitor, define anotherblock, and then run again



    • It’s still going to increase

Conclusion: __sanitizer_cov_trace_pc_guard_init monitors all methods, functions, and blocks in a project, reflecting the number of symbols in the project

  • The __sanitizer_cov_trace_pc_guard method intercepts methods in a project, the equivalent of hooks

    • intouchesBegan:In the callwushaungFunction, and then call it in functionblock



    • And then when it’s up and running, in__sanitizer_cov_trace_pc_guardFunction at the break point, and then tap the screen to triggertouchesBegan:



    • You can see by looking at the stacktouchesBegan:Will be called__sanitizer_cov_trace_pc_guardFunction that is triggered every time the breakpoint is broken.
    • You can then make a breakpoint at this method to restart the project and monitor the order in which the start methods are executed

Conclusion: __sanitizer_cov_trace_pc_guard is called by internal project methods, functions, and block functions, which Hook everything in the project

The principle of

After the project is up and running, then at the interrupt point at the touchesBegan: function, tap the screen to see the assembly



  • It was found by observation thattouchesBegan:Header implementationStack balance correlationThe code will be inserted later__sanitizer_cov_trace_pc_guardDelta function, it comes after thattouchesBegan:Implementation, and then inwushuangFunctions andblockIt’s the same at all the breaking points
  • So, just add it to the projectClangWith the staked mark, the compiler will insert all methods, functions,blockCode implementedThe edgeTo add a__sanitizer_cov_trace_pc_guardcode
  • Compiler in all code(Self-written code)To add this function, that is, to modify the binary file, only the compiler can do this
  • So before we go liveCode reviewWhen used, when on-line, willOther C FlagTo clear the mark

Get start symbol

Next we need to get the start symbol. First we know that every method now calls __sanitizer_cov_trace_pc_guard, The __builtin_return_address(0) function retrieves the last caller address of __sanitizer_cov_trace_pc_guard. The specific methods are as follows

void __sanitizer_cov_trace_pc_guard(uint32_t *guard) {
    void *PC = __builtin_return_address(0); // Get the address of the last function called
    Dl_info info; // Symbol information structure
    dladdr(PC, &info); // Store the symbolic information obtained from the PC into the structure
    
    printf("Dl_info : \nfname: %s\nfbase: %p\nsname: %s\nsaddr: %p\n\n", info.dli_fname, info.dli_fbase, info.dli_sname, info.dli_saddr);
}
Copy the code
  • Dl_info is a structure used to store symbolic information:

    • dli_fname: Indicates the current project path
    • dli_fbaseIs:MachOThe starting location of the file
    • dli_sname: symbol name
    • dli_saddr: function address
  • Dladdr stores information about the fetch function into the Dl_info structure

  • The verification results are as follows:



  • You can then print the symbol name directly with dli_sname and get the order of all the methods



Pit analysis and solutions

The following code is used to access data from multiple threads:

void __sanitizer_cov_trace_pc_guard(uint32_t *guard) {
    if(! *guard)return;
    void *PC = __builtin_return_address(0); 
    Dl_info info;
    dladdr(PC, &info);
    NSLog(@"% @", [NSThread currentThread]);
    printf("%s\n", info.dli_sname);
}

- (void)viewDidLoad {
    [super viewDidLoad];

    [self performSelectorInBackground:@selector(testWushuang) withObject:nil];
}

- (void)testWushuang {
    sleep(3);
}
Copy the code
  • The testWushuang method is obviously called in the child thread, and the print __sanitizer_cov_trace_pc_guard thread yields the following result:



    • The method called in the child thread,__sanitizer_cov_trace_pc_guardAlso in the child thread, then there will be multithreaded access, there will be data confusion. This is when you need to use itOSAtomicEnqueue()and OSAtomicDequeue()To start the method for secure access.
    • Import the header file first#import <libkern/OSAtomic.h>And thenOther C FlagThe symbol shall be changed to:-fsanitize-coverage=func,trace-pc-guard, which is addedfuncIs only used by methods, because other functions in a method are also called__sanitizer_cov_trace_pc_guardTo exclude other calls, you need to add thisflag. Other core codes are as follows:
    // Define an atomic queue
    static OSQueueHead list = OS_ATOMIC_QUEUE_INIT;
    
    // List structure
    typedef struct {
        void *pc;   // Save the obtained PC
        void *next; // point to the next node
    } Node;
    
    void __sanitizer_cov_trace_pc_guard(uint32_t *guard) {
        void *PC = __builtin_return_address(0); 
    
        Node *node = malloc(sizeof(Node)); // Allocate memory to node
        *node = (Node){PC, NULL};  // Assign PC to node's PC
    
        OSAtomicEnqueue(&list, node, offsetof(Node, next)); // Queue the new node to the next location of the previous node
    }
    
    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
        while (YES) {
            Node *node = OSAtomicDequeue(&list, offsetof(Node, next)); // Get node at the next location of the last node in the queue
            if (node == NULL) {
                break;
            }
            Dl_info info;
            dladdr(node->pc, &info); // Save PC information to info
            printf("sname: %s\n", info.dli_sname); }}Copy the code
    • At this point, you can safely get the start symbol



Generate the Order file and configure it

Put the printed startup method into wushuang. Order file, and then take it out for binary rearrangement, the code is as follows:

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    NSMutableArray<NSString *> *nameArray = [NSMutableArray array];
    while (YES) {
        Node *node = OSAtomicDequeue(&list, offsetof(Node, next));
        if (node == NULL) {
            break;
        }
        Dl_info info;
        dladdr(node->pc, &info);

        printf("sname: %s\n", info.dli_sname);
        NSString *funcName = @(info.dli_sname); // To the OC string

        BOOL isOCFunc = NO;
        if ([funcName hasPrefix:@"-"] || [funcName hasPrefix:@"+ ["]) {
            isOCFunc = YES;
        }
        // Generate the method name
        funcName = isOCFunc ? funcName : [@"_" stringByAppendingString:funcName];
        / / to heavy
        if(! [nameArray containsObject:funcName]) { [nameArray insertObject:funcName atIndex:0]; }}// Remove yourself
    [nameArray removeObject:[NSString stringWithFormat:@"%s", __func__]];

    // Write to the file
    NSString *funcString = [nameArray componentsJoinedByString:@"\n"];
    NSLog(@"\nfuncString: \n%@", funcString);
    NSString *filePath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"wushuang.order"];
    NSData *fileData = [funcString dataUsingEncoding:NSUTF8StringEncoding];
    BOOL isSuccess = [[NSFileManager defaultManager] createFileAtPath:filePath contents:fileData attributes:nil];
    if (isSuccess) {
        NSLog(@"Write success 🎉");
    } else {
        NSLog(@"Write failure 😭"); }}Copy the code
  • The sequence of methods obtained after running is



  • Select your phone at Window->Devices and Simulators, then select the current Demo, Download the Container and Download it to your desktop



    • Right-click to display package contents intempFound inwushuang.orderFile, place the file in the project root directory, and clickBuild SettingtheOrder FileThe path is configured and theWrite Link Map FileSet toYES, and finally deleteOther C FlagContent, found after runtxtFile and open it



    At this point the order and print the same!

Swift symbol acquisition

-sanitize-coverage=func, and -sanitize=undefined. The core code is as follows:

class Wushuang: NSObject {
    @objc class func say() {
        print(" wushuang say NB "); }}// ViewController.h
+ (void)load {
    [Wushuang say];
    
    block();
}
Copy the code

So you get the swift symbol